臨床医学を学ぶ上で基礎生理学が不可欠なように、Python文法は医療AIライブラリを使いこなすための土台です。このコースで学ぶ主要な概念を視覚的に整理しました。
数値やテキストを入れる「変数」(箱)と、それらをまとめる「リスト」(順序付き)や「辞書」(ラベル付き)。特に辞書は医療AIで最重要です。
臨床判断のように条件で処理を分ける「if文」と、患者リストを総回診するように処理を繰り返す「for文」。プログラムの基本的な流れを作ります。
BMI計算など、繰り返し使う処理を「関数」としてまとめます。コードが整理され再利用しやすくなり、ミスも減ります。
データと処理を一体化した「クラス」(設計図)と、それを「継承」して専門機能を加える考え方は、PyTorchなどAIライブラリの根幹をなす最重要概念です。
臨床医学の「基礎生理学」としてのPython文法
臨床医学において、個々の疾患(応用)を深く理解するためには、その根底にある「基礎生理学」(原理)の知識が不可欠です。どの臓器がどのように連携し、細胞がどう機能するかが分かって初めて、病態の理解や治療の選択が可能になります。
医療AIにおけるプログラミングも全く同じです。
PyTorchやscikit-learnといった高度なライブラリ(臨床医学)は、一見すると複雑な呪文のように見えるかもしれません。しかし、それらを構成しているのは、Pythonという言語の「文法」(基礎生理学)に他なりません。文法が分からなければ、コードが読めず、エラーが解決できず、AIモデルの挙動を理解することもできません (VanderPlas 2016)。
この回(C30.6)は、医療AIの実装という応用に進む前に、その土台となるPython文法を「最低限の努力で最大の効果が出る」ように凝縮して学ぶショートコースです。臨床医が基礎生理学の知識を頼りに複雑な病態を解き明かすように、私たちもPython文法の知識を武器に、医療AIのコードを読み解く力を手に入れます。
【実行前の準備】日本語グラフ表示のためのライブラリ
本記事のサンプルコードでグラフ(例:Matplotlib)を使用する際、日本語が文字化けすることがあります。これを防ぐため、最初のコードを実行する前に、お使いの環境(ターミナルやコマンドプロンプト)で以下のコマンドを実行し、japanize-matplotlib ライブラリをインストールしてください。
# ターミナルまたはコマンドプロンプトで実行
pip install japanize-matplotlib
1. データを格納する「ラベル」:変数と基本データ型
プログラミングにおける変数 (Variable) とは、データ(数値やテキスト)に名前を付けて、コンピュータのメモリ上に保存しておくための「箱」や「ラベル」のようなものです。臨床現場で言えば、患者さんに「患者ID」というラベルを貼り、カルテに「体温: 36.5℃」と記録する行為に似ています。
Pythonでは、データ型(データの種類)を自動で認識しますが、主に以下の4つを区別することが重要です (Python Software Foundation 2024)。
int(整数型):38(年齢)、120(心拍数)など、小数点の付かない数値。float(浮動小数点型):36.5(体温)、1.8(クレアチニン値)など、小数点の付く数値。str(文字列型):"male"(性別)、"Headache"(主訴)など、テキストデータ。bool(ブール型):True(真)またはFalse(偽)。臨床検査の「陽性/陰性」や「該当/非該当」の判断に使います。
# 【サンプルコード1】変数とデータ型
# Pythonのバージョンと主要ライブラリのバージョンを確認(再現性のため)
import sys
import platform
import numpy as np # NumPyは数値計算ライブラリ
import pandas as pd # Pandasはデータ分析ライブラリ
print(f"Python Version: {platform.python_version()}")
# Python Version: 3.11.x (実行環境により異なります)
print(f"NumPy Version: {np.__version__}")
# NumPy Version: 1.26.x (実行環境により異なります)
print(f"Pandas Version: {pd.__version__}")
# Pandas Version: 2.2.x (実行環境により異なります)
# --- 医療データの変数定義 ---
# 1. 患者ID (文字列)
patient_id = "PID_001"
# 2. 年齢 (整数)
age = 65
# 3. 体温 (浮動小数点数)
body_temperature = 38.5
# 4. 検査陽性フラグ (ブール型)
is_positive = True
# 5. 主訴 (文字列)
chief_complaint = "発熱"
# --- 変数の内容と「型」を確認 ---
# print() はコンソールに結果を表示する関数
# type() は変数のデータ型を調べる関数
print(f"患者ID: {patient_id}, 型: {type(patient_id)}")
# 出力例: 患者ID: PID_001, 型:
print(f"年齢: {age}, 型: {type(age)}")
# 出力例: 年齢: 65, 型:
print(f"体温: {body_temperature}, 型: {type(body_temperature)}")
# 出力例: 体温: 38.5, 型:
print(f"検査陽性: {is_positive}, 型: {type(is_positive)}")
# 出力例: 検査陽性: True, 型:
f-stringについて
サンプルコードの print(f"...") という記法は f-string と呼ばれます。これはPython 3.6で導入された機能 (Python Software Foundation 2015) で、文字列の中に {変数名} を直接埋め込むことができ、非常に直感的で読みやすいコードが書けます。
2. データをまとめる「容器」:コレクション型
個々のデータを変数で扱えるようになったら、次はそれらをまとめて管理する方法が必要です。例えば、一人の患者の全検査データや、病棟の全患者リストなどです。Pythonでは、以下の「コレクション型」と呼ばれる容器が用意されています。
2.1. list (リスト):順序付きのカルテ項目
リスト (list) は、複数のデータを [] で囲んで順序通りに格納する容器です。
臨床で言えば、「時系列に並んだバイタルサイン」や「検査オーダーの項目リスト」に似ています。
- 特徴: 順序があり、中身の追加、変更、削除が自由自在。
- 医療での例:
[36.5, 36.8, 37.1](体温の時系列データ)、["血液検査", "胸部X線", "CT"](オーダーリスト)。
2.2. dict (辞書):最も重要な「患者レコード」
辞書 (dict) は、医療AI開発において最も多用するデータ構造です。{} を使い、「キー (Key)」と「値 (Value)」を一対(ペア)で管理します。
- 特徴: 順序ではなく、「カルテの項目名(キー)」を使ってデータ(値)に瞬時にアクセスできる。
- 医療での例: 電子カルテの1レコードそのもの。
{"patient_id": "PID_001", "age": 65, "temperature": 38.5}のように、データに意味を持たせることができます。
2.3. tuple (タプル) と set (セット)
- タプル (
tuple):()で囲みます。リストと似ていますが、一度作成すると中身を変更できません(Immutable)。座標(128.0, 64.5)や、変更されては困る設定値(例:RGB値)に使われます。 - セット (
set):{}で囲みます(辞書と似ていますがキーがありません)。重複するデータを許さず、順序も持ちません。「ある病棟で投与された全薬剤のユニーク(重複なし)リスト」を作りたい、といった用途に使います。
# 【サンプルコード2】コレクション型
# --- 1. リスト (list) ---
# ある患者の1時間ごとの体温記録
temperatures_list = [38.5, 38.2, 37.9, 38.0]
# リストの最初の要素にアクセス (Pythonのインデックスは0から始まる)
print(f"最初の体温: {temperatures_list[0]}")
# 出力例: 最初の体温: 38.5
# リストに新しいデータを追加
temperatures_list.append(37.8)
print(f"追加後のリスト: {temperatures_list}")
# 出力例: 追加後のリスト: [38.5, 38.2, 37.9, 38.0, 37.8]
# --- 2. 辞書 (dict) ---
# 一人の患者情報を辞書で管理(キーと値のペア)
patient_record = {
"patient_id": "PID_002",
"age": 45,
"department": "循環器内科",
"vitals": [120, 80, 75] # 値にはリストも入れられる
}
# 'age' というキーを指定して値にアクセス
print(f"患者の年齢: {patient_record['age']}")
# 出力例: 患者の年齢: 45
# 新しいキーと値(主訴)を追加
patient_record["chief_complaint"] = "胸痛"
print(f"更新後のレコード: {patient_record}")
# 出力例: 更新後のレコード: {'patient_id': 'PID_002', 'age': 45, ... 'chief_complaint': '胸痛'}
# --- 3. セット (set) ---
# 複数患者が処方された薬剤リスト(重複あり)
medications = ["Aspirin", "Metformin", "Aspirin", "Losartan", "Metformin"]
# セットを使い、重複を除いた薬剤リストを作成
unique_medications = set(medications)
print(f"ユニークな薬剤リスト: {unique_medications}")
# 出力例: ユニークな薬剤リスト: {'Losartan', 'Metformin', 'Aspirin'}
3. 判断と繰り返し:制御構文
データ(生理学)を扱う容器(解剖学)を学んだら、次はそれらを使って「判断」し「処理」する方法(臨床推論)を学びます。
3.1. if文:臨床判断の分岐
if文(もし〜ならば)は、プログラミングにおける「臨床判断」です。条件に応じて処理を分岐させます。
if(もし条件AがTrueなら): 処理Aを行う。elif(そうでなく、もし条件BがTrueなら): 処理Bを行う。(Else Ifの略)else(どちらでもなければ): 処理Cを行う。
臨床判断では、「eGFR > 60 ならステージG1」「BMI > 25 なら肥満」といった基準(閾値)で判断することが多々ありますが、if文はそれをコードで表現するものです。
3.2. for文:患者リストの総回診
for文(〜の間、繰り返す)は、反復処理の主役です。「リストの全要素に対して、同じ処理を順番に行う」ときに使います。
臨床で言えば、「病棟の患者リスト(patients_list)を一人ずつ順番に(for patient in ...)見ていき、バイタルを確認する」という「総回診」のイメージです。
# 【サンプルコード3】if文とfor文
# 複数の患者レコード(辞書)をリストにまとめる
# これが医療AIの入力データの基本形
patient_list = [
{"patient_id": "PID_001", "age": 65, "hba1c": 7.0},
{"patient_id": "PID_002", "age": 45, "hba1c": 5.8},
{"patient_id": "PID_003", "age": 70, "hba1c": 8.1},
{"patient_id": "PID_004", "age": 55, "hba1c": 6.2}
]
# forループで患者リストを「総回診」
for patient in patient_list:
# patient 変数には、リストから取り出された「一人の患者の辞書」が順番に入る
print(f"--- 患者ID: {patient['patient_id']} の診断 ---")
# if文でHbA1cの値に基づいて臨床判断(分岐)
# 演算子(>=, <)を使って条件を記述
if patient["hba1c"] >= 6.5:
print(f"結果: 糖尿病の疑い (HbA1c: {patient['hba1c']})")
elif patient["hba1c"] >= 6.0:
print(f"結果: 境界型 (HbA1c: {patient['hba1c']})")
else:
print(f"結果: 正常範囲 (HbA1c: {patient['hba1c']})")
# --- 出力例 ---
# --- 患者ID: PID_001 の診断 ---
# 結果: 糖尿病の疑い (HbA1c: 7.0)
# --- 患者ID: PID_002 の診断 ---
# 結果: 正常範囲 (HbA1c: 5.8)
# ... (以下省略) ...
3.3. 内包表記:効率的なデータ処理
forループとif文を組み合わせて「リストから特定の条件を満たす要素だけを抽出して新しいリストを作る」ことは頻繁にあります。
内包表記 (Comprehension) は、この定型処理を非常に簡潔に、かつ高速に書くためのPython固有の記法です (Python Software Foundation 2024)。
# 【サンプルコード4】リスト内包表記
# 上記の患者リスト (patient_list) を使用
# 従来の方法 (for文) で、HbA1cが6.5以上の患者IDだけを抜き出す
high_risk_patients_v1 = [] # 空のリストを準備
for patient in patient_list:
if patient["hba1c"] >= 6.5:
high_risk_patients_v1.append(patient["patient_id"])
print(f"V1 (for文): {high_risk_patients_v1}")
# 出力例: V1 (for文): ['PID_001', 'PID_003']
# 内包表記を使った方法
# [実行する処理 for 変数 in リスト if 条件] という語順
high_risk_patients_v2 = [
patient["patient_id"]
for patient in patient_list
if patient["hba1c"] >= 6.5
]
print(f"V2 (内包表記): {high_risk_patients_v2}")
# 出力例: V2 (内包表記): ['PID_001', 'PID_003']
データ分析ライブラリPandas (VanderPlas 2016) では、この内包表記の考え方が多用されます。
4. 処理をまとめる「標準手順書」:関数 (Function)
同じ計算(例えばBMIの計算)をコードのあちこちで行うのは非効率であり、計算式を間違えたときに全ての箇所を修正する必要があり、医療安全上も危険です。
関数 (Function) は、一連の処理を「標準手順書(SOP)」としてまとめ、名前(calculate_bmiなど)を付けて再利用可能にする仕組みです (Lutz 2013)。
- 引数 (Argument): 関数に渡す入力データ(例:身長、体重)。
- 戻り値 (Return Value): 関数が計算して返す結果(例:BMI値)。
# 【サンプルコード5】関数
import math # mathライブラリの pow() (べき乗) を使うため
# --- 1. 関数の定義 (標準手順書の作成) ---
# def で関数を定義
def calculate_bmi(height_m: float, weight_kg: float) -> float:
"""
身長(m)と体重(kg)からBMIを計算する。
Args:
height_m (float): 身長 (メートル単位)
weight_kg (float): 体重 (キログラム単位)
Returns:
float: 計算されたBMI値。身長が0以下の場合は 0.0 を返す。
"""
# PEP 484 (Python Software Foundation 2014) に基づく「型ヒント」
# (height_m: float) -> float は、入力と出力の「型」を明記し、
# 医療安全でいう「5R」のように、コードの安全性を高める。
# docstring (関数直下の """...""") は、
# この関数が何をするかの説明書(添付文書)となる。
# ゼロ除算エラーを防ぐためのチェック (例外処理の代わり)
if height_m <= 0:
return 0.0 # 身長が0以下なら 0.0 を返す (処理を終了)
# BMI = 体重 / (身長 * 身長)
bmi = weight_kg / pow(height_m, 2) # pow(X, 2) は Xの2乗
return bmi # return で計算結果を返す
# --- 2. 関数の使用 (手順書の実行) ---
height_data = 1.75
weight_data = 70.0
# 関数を呼び出し、戻り値を受け取る
bmi_result = calculate_bmi(height_data, weight_data)
# f-stringで小数点以下1桁まで表示 (:.1f)
print(f"身長 {height_data}m, 体重 {weight_data}kg のBMIは {bmi_result:.1f} です。")
# 出力例: 身長 1.75m, 体重 70.0kg のBMIは 22.9 です。
5. データを包む「設計図」:クラスとオブジェクト指向
ここまでの学習で、データを入れる「変数」、データをまとめる「リスト」や「辞書」、そして処理をまとめる「関数」を学びました。これらはプログラミングの基本的な部品です。しかし、実際の医療現場のように複雑な状況を扱うには、これらの部品をバラバラに管理するだけでは限界がきます。
例えば、一人の患者さんを考えてみましょう。その方には「患者ID、氏名、年齢、既往歴リスト」といったデータがあります。そして、そのデータを使って「BMIを計算する」「新しい処方薬を追加する」「アレルギー情報を確認する」といった処理が行われます。
この「データ」と「それに関連する処理」は、本来切り離せないセットのはずです。このセットを、プログラムの世界でひとつの塊として扱うための設計思想がオブジェクト指向プログラミング (Object-Oriented Programming, OOP) であり、その中核をなすのがクラス (Class) なのです (Gamma et al. 1994)。
- クラス (Class): 「患者」という概念の設計図。どんなデータ(属性)を持ち、どんな処理(メソッド)ができるかを定義します。
- オブジェクト (Object) / インスタンス (Instance): 設計図から作られた実体。例えば、「ID: PID_005, 氏名: 田中太郎さん」という具体的な一人の患者データがオブジェクトです。
クラスは、単なるコードの書き方のひとつではなく、複雑な問題を整理し、安全で再利用しやすいプログラムを作るための強力な「考え方」そのものだと言えます。
5.1. クラスの基本:データと処理を一体化する
クラスを使うと、関連するデータ(属性)と処理(メソッド)を「患者」というひとつの単位にまとめることができます。これにより、コードの見通しが格段に良くなり、まるで現実世界の概念をそのままコンピュータの中に持ち込んだかのように、直感的にプログラムを組み立てることが可能になります。
以下のサンプルコードでは、「Patient」というクラス(設計図)を定義し、そこから patient_A と patient_B という2人の患者(インスタンス)を作成しています。
【サンプルコード6】クラス:患者情報の設計図
# 【サンプルコード6】クラス
# --- 1. クラスの定義 (設計図の作成) ---
class Patient:
"""
患者情報を管理するためのクラス(設計図)
"""
# --- 1.1 コンストラクタ (__init__) ---
# インスタンス(実体)が作成されるときに「必ず」最初に実行される特別なメソッド。
# 新しいカルテが作られる瞬間に、必須項目を書き込むイメージ。
def __init__(self, patient_id: str, age: int):
# self は「このインスタンス自身」を指す特別な変数。
# 「このカルテの...」と指定するようなもの。
# --- 属性 (Attribute) の定義 ---
# インスタンスが保持するデータ。
self.patient_id = patient_id # このインスタンスの患者ID
self.age = age # このインスタンスの年齢
self.medications = [] # このインスタンスの処方薬リスト(最初は空)
print(f"患者 {self.patient_id} (年齢 {self.age}) のレコードを作成しました。")
# --- 1.2 メソッド (Method) の定義 ---
# インスタンスが行える処理。
# 処方薬を追加するメソッド
def add_medication(self, med_name: str):
# self.medications という「このインスタンスの処方薬リスト」に薬を追加する。
self.medications.append(med_name)
print(f"{self.patient_id} に {med_name} を追加しました。")
# 現在の処方薬リストを表示するメソッド
def show_medications(self):
if not self.medications: # もし self.medications リストが空ならば
print(f"{self.patient_id} は現在処方薬がありません。")
else:
# ', '.join() は、リストの各要素をカンマとスペースで連結して一つの文字列にする。
print(f"{self.patient_id} の処方薬: {', '.join(self.medications)}")
# --- 2. クラスの使用 (インスタンスの作成) ---
# Patientクラス(設計図)から、2人の患者オブジェクト(実体)を作成
patient_A = Patient(patient_id="PID_005", age=68)
patient_B = Patient(patient_id="PID_006", age=52)
# --- 3. メソッドの実行 ---
# patient_A インスタンスに属するメソッドを呼び出す
patient_A.add_medication("Aspirin")
patient_A.add_medication("Losartan")
# patient_B インスタンスに属するメソッドを呼び出す
patient_B.add_medication("Metformin")
# それぞれのインスタンスの状態を確認
patient_A.show_medications()
patient_B.show_medications()
【出力例】
患者 PID_005 (年齢 68) のレコードを作成しました。
患者 PID_006 (年齢 52) のレコードを作成しました。
PID_005 に Aspirin を追加しました。
PID_005 に Losartan を追加しました。
PID_006 に Metformin を追加しました。
PID_005 の処方薬: Aspirin, Losartan
PID_006 の処方薬: Metformin
このコードのポイントは、patient_A の処方薬リストと patient_B の処方薬リストが、完全に独立して管理されている点です。patient_A.add_medication() を実行しても、patient_B のデータには何の影響もありません。これは、クラスによってデータと処理がカプセルのように包まれ、オブジェクトごとに安全に管理されているからです。これをカプセル化 (Encapsulation) と呼び、オブジェクト指向の重要な特徴の一つです。
5.2. AI開発の要「継承」:設計図の再利用と拡張
さて、ここからが医療AI開発において極めて重要になる話です。
先ほどの Patient クラスは、一般的な患者の情報を管理するのには十分でした。しかし、もし「糖尿病患者」のように、特別な管理項目や専門的な処理が必要な患者を扱いたい場合はどうでしょう?
もちろん、新しく DiabeticPatient というクラスをゼロから作ることもできます。しかし、そのクラスにも患者IDや年齢は必要ですし、処方薬を追加する機能も同じように必要です。同じコードを何度も書くのは非効率で、間違いの元になります。
そこで登場するのが継承 (Inheritance) です。
継承とは、あるクラス(親クラス)の機能(属性とメソッド)をすべて受け継いだ、新しいクラス(子クラス)を作ることができる仕組みです。子クラスでは、親の機能はそのままに、新しい機能を追加したり、一部の機能を専門的に作り変えたり(オーバーライド)することができます。
これは、医療で言えば「一般診療科」の知識をベースに、「循環器内科」や「呼吸器内科」といった専門分野が成り立っている関係に似ています。
【サンプルコード7】継承:専門的な患者クラスの作成
# 【サンプルコード7】継承
# 親クラス (サンプルコード6と同じ)
class Patient:
def __init__(self, patient_id: str, age: int):
self.patient_id = patient_id
self.age = age
self.medications = []
print(f"患者 {self.patient_id} (年齢 {self.age}) の基本レコードを作成。")
def add_medication(self, med_name: str):
self.medications.append(med_name)
print(f"{self.patient_id} に {med_name} を追加。")
def show_medications(self):
if not self.medications:
print(f"[{self.patient_id}] 処方薬: なし")
else:
print(f"[{self.patient_id}] 処方薬: {', '.join(self.medications)}")
# --- 子クラスの定義 ---
# class 子クラス名(親クラス名): と書くことで継承する
class DiabeticPatient(Patient):
"""
Patientクラスを継承した、糖尿病患者のための専門クラス
"""
def __init__(self, patient_id: str, age: int, initial_hba1c: float):
# --- 親クラスの初期化 ---
# super().__init__() で、親クラス(Patient)の__init__メソッドを呼び出す。
# これにより、patient_idとageが親クラスで正しく設定される。
super().__init__(patient_id, age)
# --- 子クラス独自の属性を追加 ---
self.hba1c_log = [initial_hba1c] # HbA1cの記録リスト
print(f"-> 糖尿病患者用の専門レコードに拡張。初期HbA1c: {initial_hba1c}")
# --- 子クラス独自のメソッドを追加 ---
def add_hba1c_record(self, new_hba1c: float):
self.hba1c_log.append(new_hba1c)
print(f"[{self.patient_id}] 新しいHbA1c値 ({new_hba1c}) を記録しました。")
def show_current_status(self):
# 親クラスのメソッド(show_medications)をそのまま利用できる
self.show_medications()
# さらに、子クラス独自の情報を表示
print(f"[{self.patient_id}] 最新のHbA1c: {self.hba1c_log[-1]}")
# --- 継承したクラスの使用 ---
# DiabeticPatientクラスからインスタンスを作成
patient_C = DiabeticPatient(patient_id="PID_007", age=58, initial_hba1c=7.8)
# 親クラスから継承したメソッドが使えることを確認
patient_C.add_medication("Metformin")
patient_C.add_medication("Insulin")
# 子クラスで追加したメソッドが使えることを確認
patient_C.add_hba1c_record(7.5)
# 拡張された機能を確認
patient_C.show_current_status()
【出力例】
患者 PID_007 (年齢 58) の基本レコードを作成。
-> 糖尿病患者用の専門レコードに拡張。初期HbA1c: 7.8
PID_007 に Metformin を追加。
PID_007 に Insulin を追加。
[PID_007] 新しいHbA1c値 (7.5) を記録しました。
[PID_007] 処方薬: Metformin, Insulin
[PID_007] 最新のHbA1c: 7.5
5.3. なぜ継承が医療AIで重要なのか?
この「継承」の考え方は、私たちが普段使っている医療AIライブラリの根幹をなすものです。
例えば、深層学習ライブラリのPyTorchを使って画像診断AIモデルを構築する場合を考えます。PyTorchでは、すべてのカスタムAIモデルは torch.nn.Module という基本的な親クラスを継承して作成することが「お作法」になっています。
import torch
import torch.nn as nn
# torch.nn.Module という親クラスを継承する
class SimpleCNN(nn.Module):
def __init__(self):
# まず親クラスの__init__を呼び出す (お決まり)
super().__init__()
# ここに、自分だけのモデル構造(層)を定義する
self.conv1 = nn.Conv2d(...)
self.relu = nn.ReLU()
# (以下略)
# モデルがデータを受け取ったときの処理を定義する
def forward(self, x):
# ここに、自分だけのデータ処理フローを記述する
x = self.conv1(x)
x = self.relu(x)
# (以下略)
return x
この仕組みの何が素晴らしいのでしょうか?
それは、私たちが torch.nn.Module を継承するだけで、モデルの学習ループ、パラメータの最適化、GPUへのデータ転送といった、AIモデルに共通して必要となる膨大で複雑な基本機能を、PyTorchが自動的に提供してくれる点にあります。
私たちは、親クラスが用意してくれた土台の上で、最も本質的で創造的な部分、すなわち「どんなネットワーク構造にするか (__init__)」と「データをどう処理するか (forward)」という専門的な部分の設計にだけ集中すればよいのです。
これは、scikit-learnのような機械学習ライブラリでも同様です。独自の推定器(モデル)を作る際は、BaseEstimator などの親クラスを継承することで、ライブラリが提供するパイプラインやグリッドサーチといった便利な機能とシームレスに連携できるようになります。
結論として、クラス、特に「継承」を理解することは、既存のAIツールを単に使うだけでなく、その内部構造を理解し、自分の目的に合わせてカスタマイズしたり、新しいモデルを構築したりするための必須の知識なのです。医療における深層学習の応用は、このようなフレームワークの力を借りることで急速に進展しました (Esteva et al. 2019)。クラスは、その発展を支える、まさに縁の下の力持ちと言えるでしょう。
6. 安全な運用:例外処理とモジュール
6.1. try...except:予期せぬ事態への備え
臨床現場では予期せぬ事態(データ欠損、フォーマットエラー)が常に発生します。例えば、"N/A"(該当なし)と入力された検査値を、数値(float)に変換しようとするとエラーが発生し、プログラム全体が停止してしまいます。
例外処理 (try...except) は、こうしたエラー(例外)が発生しても、プログラムを停止させずに安全に対応(ハンドリング)するための構文です。
# 【サンプルコード7】例外処理
data_entry = ["38.5", "37.2", "N/A", "36.8"]
temperatures_numeric = []
for entry in data_entry:
try:
# --- tryブロック ---
# エラーが発生する「可能性」のある処理
value = float(entry) # 文字列を浮動小数点数に変換
temperatures_numeric.append(value)
except ValueError:
# --- exceptブロック ---
# ValueError (型変換エラー) が発生した場合に実行される
print(f"エラー: '{entry}' は数値に変換できません。欠損値として扱います。")
# ここでは欠損値の代わりに 0.0 を追加する (例)
temperatures_numeric.append(0.0)
print(f"変換後の数値リスト: {temperatures_numeric}")
# 出力例:
# エラー: 'N/A' は数値に変換できません。欠損値として扱います。
# 変換後の数値リスト: [38.5, 37.2, 0.0, 36.8]
6.2. コードの部品化と安全な実行:import と if __name__ == "__main__"
C30.3で触れたように、実際の医療AI開発では、すべてのコードを一つの巨大なファイルに書くことはありません。それは、大学病院の全機能を一つの巨大な部屋で済ませようとするようなもので、すぐに混乱し、管理不能になってしまいます。
現実の病院が「内科」「外科」「放射線科」「検査部」と機能ごとに部署を分けているように、プログラムも機能ごとにファイルを分割します。
utils.py: BMI計算など、よく使う便利な関数(道具)をまとめたファイル。models.py: AIモデルの設計図(クラス)をまとめたファイル。main.py: プログラム全体の流れを制御し、各部品を呼び出して実行する中心的なファイル。
このようにコードを機能ごとにファイルへ分割し、再利用可能な部品にすることをモジュール化と呼びます。このモジュール化を実現するための重要な仕組みが import と if __name__ == "__main__" です。
import:専門部署の機能を呼び出す
import は、別のファイル(モジュール)で定義された関数やクラスを、現在のファイルで「輸入」して使えるようにする命令です。これは、主治医が「検査部に血液検査を依頼する」「放射線科にCT画像の読影を依頼する」のに似ています。自分の専門外の仕事は、その道のプロに任せるわけです。
なぜimportが重要なのか?
- 再利用性の向上:
utils.pyにcalculate_bmi関数を一度作っておけば、どんなプロジェクトからでもimportして使えます。同じコードを何度も書く必要がありません。 - 保守性の向上: BMIの計算式に修正が必要になった場合、
utils.pyの1箇所を直すだけで、それを使っているすべてのプログラムに修正が反映されます。コードが分散していないため、修正漏れやバグのリスクが大幅に減ります。 - 可読性の向上:
main.pyは全体の流れに集中でき、個々の細かい処理は各モジュールに任せることで、コードがすっきりと読みやすくなります。
import の使い方
具体的なファイル構成で見てみましょう。
ファイル構成:
my_project/
├── main.py # メインの実行ファイル
└── utils.py # 便利関数を置くファイル
utils.py の中身:
# utils.py
def calculate_bmi(height_m: float, weight_kg: float) -> float:
"""身長(m)と体重(kg)からBMIを計算する関数"""
if height_m <= 0:
return 0.0
return weight_kg / (height_m ** 2)
def check_vital_signs(bp_systolic: int, heart_rate: int):
"""バイタルサインの簡単なチェック"""
print(f"血圧: {bp_systolic}, 心拍数: {heart_rate}")
main.py で utils.py の機能を使う:
# main.py
# --- 方法1: モジュール全体をインポート ---
import utils
# 「utilsモジュール」の「calculate_bmi関数」を呼び出す
bmi1 = utils.calculate_bmi(1.75, 70.0)
print(f"方法1のBMI: {bmi1:.1f}")
# --- 方法2: 特定の関数だけをインポート ---
from utils import calculate_bmi
# 関数名を直接呼び出せる
bmi2 = calculate_bmi(1.60, 55.0)
print(f"方法2のBMI: {bmi2:.1f}")
# --- 方法3: 別名 (エイリアス) をつけてインポート ---
# 長いモジュール名や、他のライブラリと名前が被る場合によく使う
import utils as u
u.check_vital_signs(120, 75)
医療AI開発で頻繁に目にする import numpy as np や import pandas as pd は、この方法3の典型例です。これにより、コード中で numpy.array() と長く書く代わりに np.array() と簡潔に書けるようになります。
if __name__ == "__main__":薬剤の添付文書と本体の区別
さて、次に非常に重要でありながら、初学者がつまずきやすい「おまじない」についてです。if __name__ == "__main__"
これは一体何をしているのでしょうか?
utils.py のような部品ファイルを作っているとき、その関数が正しく動くかテストしたいことがあります。例えば、utils.py の末尾にテストコードを書いたとしましょう。
utils.py (テストコード追加版):
# utils.py
def calculate_bmi(height_m: float, weight_kg: float) -> float:
# (関数の中身は同じ)
if height_m <= 0:
return 0.0
return weight_kg / (height_m ** 2)
# --- このファイル単体で動くかテストするためのコード ---
print("--- utils.py のテストを実行します ---")
test_bmi = calculate_bmi(1.80, 80)
print(f"テストケース (1.80m, 80kg) のBMI: {test_bmi:.1f}")
この状態で、先ほどの main.py を実行すると、何が起こるでしょうか?
main.py を実行したときの出力:
--- utils.py のテストを実行します ---
テストケース (1.80m, 80kg) のBMI: 24.7
方法1のBMI: 22.9
方法2のBMI: 21.5
血圧: 120, 心拍数: 75
main.py はただBMI計算の「部品」を使いたいだけなのに、utils.py に書かれたテストコードまで実行されてしまいました。これは意図しない動作です。
ここで if __name__ == "__main__" の出番です。
Pythonには __name__ という特殊な変数が組み込まれており、そのファイルがどのように実行されたかによって中身が変わります。
- 直接実行された場合: ターミナルで
python utils.pyのように直接実行されると、そのファイルにとって__name__変数の中身は文字列"__main__"になります。「私が主役(メイン)です」という意味です。 importされた場合:main.pyの中でimport utilsのように部品として読み込まれると、utils.pyにとって__name__変数の中身はモジュール名である文字列"utils"になります。「私は部品(utils)です」という意味です。
この性質を利用して、「このファイルが直接実行されたときだけ、この中のコードを実行してね」という条件分岐を書くのが if __name__ == "__main__" です。
utils.py (改良版):
# utils.py (改良版)
def calculate_bmi(height_m: float, weight_kg: float) -> float:
# (関数の中身は同じ)
if height_m <= 0:
return 0.0
return weight_kg / (height_m ** 2)
# --- このファイルが「直接実行された」場合にのみ、以下のブロックを実行 ---
if __name__ == "__main__":
print("--- utils.py を単体でテスト実行します ---")
test_bmi = calculate_bmi(1.80, 80)
print(f"テストケース (1.80m, 80kg) のBMI: {test_bmi:.1f}")
この改良版 utils.py を使って、2つのパターンで実行してみましょう。
パターン1: main.py から import して実行
# main.py を実行
$ python main.py
出力:
方法1のBMI: 22.9
方法2のBMI: 21.5
血圧: 120, 心拍数: 75
今度は utils.py のテストコードは実行されず、意図通り部品として機能しました。
パターン2: utils.py を直接実行
# utils.py を直接実行
$ python utils.py
出力:
--- utils.py を単体でテスト実行します ---
テストケース (1.80m, 80kg) のBMI: 24.7
utils.py を単体でテストしたいときは、テストコードが正しく実行されました。
なぜこれが重要なのか?
この仕組みにより、一つのPythonファイルに2つの役割を持たせることができます。
- ライブラリとしての役割: 他のファイルから
importされて、関数やクラスといった「部品」を提供する。 - スクリプトとしての役割: 直接実行されて、テストやデモンストレーションといった自己完結した処理を行う。
これは、チームで大規模な開発を行う際や、作成したツールを他の人に配布する際に、非常に重要な作法となります。Pythonの標準ライブラリや、私たちが使うあらゆるAIライブラリは、この原則に基づいて作られています (Python Software Foundation 2024)。
この「モジュール化」と「安全な実行」の作法を身につけることは、単にコードを書ける段階から、再利用可能で信頼性の高いソフトウェアを設計できる段階へとステップアップするための、大きな一歩なのです。
7. まとめ:文法がAIコードの読解力に変わる
本講義(C30.6)では、医療AIという応用分野の根底を支える「基礎生理学」として、Pythonの主要文法を駆け足で学びました。
一見すると地味な文法(変数、if、for、関数、クラス)が、複雑なAIモデル(例えばPyTorch)の中でどのように使われているか、その断片が見えてきたはずです。
次回C30.7では、これらのコードを安全かつ再現可能に管理するための「バージョン管理システム」であるGitとGitHubについて学びます。
参考文献
- Esteva, A., Robicquet, A., Shivani, A., et al. (2019). A guide to deep learning in healthcare. Nature Medicine, 25(1), 24–29.
- Lutz, M. (2013). Learning Python, 5th Edition. O’Reilly Media.
- Python Software Foundation (2013). PEP 8 – Style Guide for Python Code. [online] Available at: https://peps.python.org/pep-0008/ (Accessed: 18 November 2025).
- Python Software Foundation (2014). PEP 484 – Type Hints. [online] Available at: https://peps.python.org/pep-0484/ (Accessed: 18 November 2025).
- Python Software Foundation (2015). PEP 498 – Literal String Interpolation. [online] Available at: https://peps.python.org/pep-0498/ (Accessed: 18 November 2025).
- Python Software Foundation (2024). The Python Language Reference. [online] Available at: https://docs.python.org/3/reference/ (Accessed: 18 November 2025).
- Van Rossum, G. (2009). A brief, timeline-oriented history of Python. [online] Available at: https://www.python.org/doc/essays/history/ (Accessed: 18 November 2025).
- VanderPlas, J. (2016). Python Data Science Handbook: Essential Tools for Working with Data. O’Reilly Media. [online] Available at: https://jakevdp.github.io/PythonDataScienceHandbook/ (Accessed: 18 November 2025).
- Esteva, A., Robicquet, A., Shivani, A., et al. (2019). A guide to deep learning in healthcare. Nature Medicine, 25(1), 24–29.
- Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional.
- Lutz, M. (2013). Learning Python, 5th Edition. O’Reilly Media.
- Paszke, A., Gross, S., Massa, F., et al. (2019). PyTorch: An Imperative Style, High-Performance Deep Learning Library. In: Advances in Neural Information Processing Systems 32 (pp. 8024-8035). Curran Associates, Inc.
- Pedregosa, F., Varoquaux, G., Gramfort, A., et al. (2011). Scikit-learn: Machine Learning in Python. Journal of Machine Learning Research, 12, 2825-2830.
- Python Software Foundation (2024). 6. Modules — Python 3.12.4 documentation. [online] Available at: https://docs.python.org/3/tutorial/modules.html (Accessed: 19 August 2024).
※本記事は情報提供を目的としたものであり、特定の治療法を推奨するものではありません。健康に関するご懸念やご相談は、必ず専門の医療機関にご相談ください。
※本記事で示されるコードスニペットおよびデータ例は、教育・解説目的で作成された架空のものであり、実在の個人や臨床データを示すものではありません。
※本記事で引用される文献は、執筆時点(2025年)でアクセス可能かつ信頼できると判断された情報源に基づいています。AIおよび医療の分野は急速に進歩しており、最新の情報については別途ご確認いただく必要があります。
ご利用規約(免責事項)
当サイト(以下「本サイト」といいます)をご利用になる前に、本ご利用規約(以下「本規約」といいます)をよくお読みください。本サイトを利用された時点で、利用者は本規約の全ての条項に同意したものとみなします。
第1条(目的と情報の性質)
- 本サイトは、医療分野におけるAI技術に関する一般的な情報提供および技術的な学習機会の提供を唯一の目的とします。
- 本サイトで提供されるすべてのコンテンツ(文章、図表、コード、データセットの紹介等を含みますが、これらに限定されません)は、一般的な学習参考用であり、いかなる場合も医学的な助言、診断、治療、またはこれらに準ずる行為(以下「医行為等」といいます)を提供するものではありません。
- 本サイトのコンテンツは、特定の製品、技術、または治療法の有効性、安全性を保証、推奨、または広告・販売促進するものではありません。紹介する技術には研究開発段階のものが含まれており、その臨床応用には、さらなる研究と国内外の規制当局による正式な承認が別途必要です。
- 本サイトは、情報提供を目的としたものであり、特定の治療法を推奨するものではありません。健康に関するご懸念やご相談は、必ず専門の医療機関にご相談ください。
第2条(法令等の遵守)
利用者は、本サイトの利用にあたり、医師法、医薬品、医療機器等の品質、有効性及び安全性の確保等に関する法律(薬機法)、個人情報の保護に関する法律、医療法、医療広告ガイドライン、その他関連する国内外の全ての法令、条例、規則、および各省庁・学会等が定める最新のガイドライン等を、自らの責任において遵守するものとします。これらの適用判断についても、利用者が自ら関係各所に確認するものとし、本サイトは一切の責任を負いません。
第3条(医療行為における責任)
- 本サイトで紹介するAI技術・手法は、あくまで研究段階の技術的解説であり、実際の臨床現場での診断・治療を代替、補助、または推奨するものでは一切ありません。
- 医行為等に関する最終的な判断、決定、およびそれに伴う一切の責任は、必ず法律上その資格を認められた医療専門家(医師、歯科医師等)が負うものとします。AIによる出力を、資格を有する専門家による独立した検証および判断を経ずに利用することを固く禁じます。
- 本サイトの情報に基づくいかなる行為によって利用者または第三者に損害が生じた場合も、本サイト運営者は一切の責任を負いません。実際の臨床判断に際しては、必ず担当の医療専門家にご相談ください。本サイトの利用によって、利用者と本サイト運営者の間に、医師と患者の関係、またはその他いかなる専門的な関係も成立するものではありません。
第4条(情報の正確性・完全性・有用性)
- 本サイトは、掲載する情報(数値、事例、ソースコード、ライブラリのバージョン等)の正確性、完全性、網羅性、有用性、特定目的への適合性、その他一切の事項について、何ら保証するものではありません。
- 掲載情報は執筆時点のものであり、予告なく変更または削除されることがあります。また、技術の進展、ライブラリの更新等により、情報は古くなる可能性があります。利用者は、必ず自身で公式ドキュメント等の最新情報を確認し、自らの責任で情報を利用するものとします。
第5条(AI生成コンテンツに関する注意事項)
本サイトのコンテンツには、AIによる提案を基に作成された部分が含まれる場合がありますが、公開にあたっては人間による監修・編集を経ています。利用者が生成AI等を用いる際は、ハルシネーション(事実に基づかない情報の生成)やバイアスのリスクが内在することを十分に理解し、その出力を鵜呑みにすることなく、必ず専門家による検証を行うものとします。
第6条(知的財産権)
- 本サイトを構成するすべてのコンテンツに関する著作権、商標権、その他一切の知的財産権は、本サイト運営者または正当な権利を有する第三者に帰属します。
- 本サイトのコンテンツを引用、転載、複製、改変、その他の二次利用を行う場合は、著作権法その他関連法規を遵守し、必ず出典を明記するとともに、権利者の許諾を得るなど、適切な手続きを自らの責任で行うものとします。
第7条(プライバシー・倫理)
本サイトで紹介または言及されるデータセット等を利用する場合、利用者は当該データセットに付随するライセンス条件および研究倫理指針を厳格に遵守し、個人情報の匿名化や同意取得の確認など、適用される法規制に基づき必要とされるすべての措置を、自らの責任において講じるものとします。
第8条(利用環境)
本サイトで紹介するソースコードやライブラリは、執筆時点で特定のバージョンおよび実行環境(OS、ハードウェア、依存パッケージ等)を前提としています。利用者の環境における動作を保証するものではなく、互換性の問題等に起因するいかなる不利益・損害についても、本サイト運営者は責任を負いません。
第9条(免責事項)
- 本サイト運営者は、利用者が本サイトを利用したこと、または利用できなかったことによって生じる一切の損害(直接損害、間接損害、付随的損害、特別損害、懲罰的損害、逸失利益、データの消失、プログラムの毀損等を含みますが、これらに限定されません)について、その原因の如何を問わず、一切の法的責任を負わないものとします。
- 本サイトの利用は、学習および研究目的に限定されるものとし、それ以外の目的での利用はご遠慮ください。
- 本サイトの利用に関連して、利用者と第三者との間で紛争が生じた場合、利用者は自らの費用と責任においてこれを解決するものとし、本サイト運営者に一切の迷惑または損害を与えないものとします。
- 本サイト運営者は、いつでも予告なく本サイトの運営を中断、中止、または内容を変更できるものとし、これによって利用者に生じたいかなる損害についても責任を負いません。
第10条(規約の変更)
本サイト運営者は、必要と判断した場合、利用者の承諾を得ることなく、いつでも本規約を変更することができます。変更後の規約は、本サイト上に掲載された時点で効力を生じるものとし、利用者は変更後の規約に拘束されるものとします。
第11条(準拠法および合意管轄)
本規約の解釈にあたっては、日本法を準拠法とします。本サイトの利用および本規約に関連して生じる一切の紛争については、東京地方裁判所を第一審の専属的合意管轄裁判所とします。
For J³, may joy follow you.

