1. リスト内包表記の基本
内包表記(comprehension)とは、リストを作るための for ループを1行に凝縮した書き方です。
[式 for 変数 in イテラブル] という形で書きます。
通常の for ループと比べて主に3つのメリットがあります。 ①短く書ける(数行 → 1行)、 ②意図が読みやすい(「このリストを作る処理だ」と一目でわかる)、 ③実行速度が速い(内部的に最適化されている)。 データサイエンスや機械学習のコードでは for ループより内包表記の方を好む慣習があります。
ただし複雑な処理を無理やり1行に詰め込むと逆に読みにくくなります。 シンプルな変換・フィルタリングには内包表記、複雑なロジックには素直に for ループ という使い分けが基本です。
2. 条件付き内包表記
[式 for 変数 in イテラブル if 条件] で、条件を満たす要素だけを取り出しながら変換できます。
データのクリーニングや異常値除去でよく使います。
💡 三項演算子との組み合わせ:
[a if 条件 else b for x in lst] と [a for x in lst if 条件] は意味が違います。
前者は全要素に対して a か b を選ぶ(要素数は変わらない)。
後者は条件を満たす要素だけを取り出す(要素数が変わる)。
3. 辞書・集合内包表記
{key: value for ...} で辞書内包表記、{式 for ...} で集合内包表記が書けます。
辞書の逆引きや重複除去に便利です。
💡 集合内包表記がなぜ重複を削除するのか:
{l for l in all_labels} の {} はコロンなし(値のみ)なので set(集合)を作る構文です(レッスン7「タプルと集合」で学んだ通り、set は重複を持ちません)。
ループしながら要素を追加していきますが、set の性質上、すでに存在する値は自動的に無視されます。
all_labels = [0, 1, 2, 0, 1, 3, 2, 1, 0, 4]
{l for l in all_labels} # → {0, 1, 2, 3, 4}(集合内包表記 → 重複が自動で消える)
set(all_labels) # 同じ結果(単純に重複を消すだけならこちらが簡潔)
# 「加工しながら重複も除去したい」場合が集合内包表記の出番
{l * 10 for l in all_labels} # → {0, 10, 20, 30, 40}(10倍しつつ重複も除去)
4. ネスト内包表記
内包表記の中に for をもう1つ書くと、2重ループを1行で表現できます。
2次元リスト(行列)の生成・展開(flatten)・フィルタリングに使います。
構造を理解する最短の方法は「まず for ループで書いてから内包表記に変換する」です。
ネスト内包表記の for ... for ... の順番は、
外側のループ → 内側のループの順(通常の for ループのネストと同じ順序)に対応しています。
💡 ネスト内包表記 = for ループを内側から外側に読む:
for ... for ... が続く場合、左から右の順(外側→内側)がそのままネストした for ループと対応します。
# ③ の等価な for ループ(こちらを先に読むと構造が分かる)
low_readings = []
for i, readings in enumerate(batch_data): # 外側
for spo2 in readings: # 内側
if spo2 < 95: # 条件
low_readings.append((f"患者{i+1}", spo2))
# ↓ 内包表記版(上を1行にまとめた形)
low_readings = [
(f"患者{i+1}", spo2)
for i, readings in enumerate(batch_data)
for spo2 in readings
if spo2 < 95
]
難しく感じたら「for ループで書いてから内包表記に変換する」という手順で練習すると理解が深まります。
2段階以上のネストは読みにくくなるため、複雑な処理は素直に for ループを使う方が保守しやすいコードになります。
5. 練習問題
検査値リストを内包表記で正規化する
リスト内包表記を使って、下の血糖値を Min-Max 正規化した新しいリストを1行で作ってください。
ヒントを見る(答え+解説)
glucose = [110, 98, 125, 88, 142, 103]
normalized = [(v - min(glucose)) / (max(glucose) - min(glucose)) for v in glucose]
print([f"{v:.3f}" for v in normalized])
# → ['0.407', '0.185', '0.685', '0.000', '1.000', '0.278']
内包表記 [式 for 変数 in リスト] を使うと、ループと計算を1行で書けます。正規化の分子・分母で min()/max() を直接使うのが簡潔で読みやすいコツです。
辞書内包表記でラベル→クラス名マップを作る
ラベルとクラス名の2つのリストから、辞書内包表記を使って {ラベル番号: クラス名} の辞書を1行で作ってください。
ヒントを見る(答え+解説)
labels = [0, 1, 2, 3]
class_names = ["正常", "結節あり", "腫瘤あり", "その他"]
label_map = {k: v for k, v in zip(labels, class_names)}
print(label_map)
print("ラベル2:", label_map[2])
# → {0: '正常', 1: '結節あり', 2: '腫瘤あり', 3: 'その他'}
# → ラベル2: 腫瘤あり
zip() で2つのリストをペアにしてから辞書内包表記に渡すと、「ラベル番号→クラス名」のマップを1行で作れます。機械学習のクラス名変換で定番のパターンです。
陰性患者のスコアだけを内包表記で抽出する
下のリストから label == 0(陰性)の患者の score だけを抽出したリストを内包表記で1行で作ってください。
ヒントを見る(答え+解説)
results = [
{"id": "P001", "label": 1, "score": 0.92},
{"id": "P002", "label": 0, "score": 0.18},
{"id": "P003", "label": 1, "score": 0.85},
{"id": "P004", "label": 0, "score": 0.31},
{"id": "P005", "label": 0, "score": 0.22},
]
neg_scores = [r["score"] for r in results if r["label"] == 0]
print("陰性スコア:", neg_scores)
# → 陰性スコア: [0.18, 0.31, 0.22]
内包表記の末尾に if 条件 を加えると、フィルタリングと値抽出を同時に1行で行えます。[r["score"] for r in results if r["label"] == 0] は「陰性の患者のスコアだけ取り出す」という意味で、AIの評価分析でよく使うパターンです。
まとめ
このレッスンのポイント
[式 for x in lst]がリスト内包表記の基本形[式 for x in lst if 条件]でフィルタリングを同時に行える[a if 条件 else b for x in lst]で全要素に対して条件分岐変換できる{k: v for ...}が辞書内包表記。逆引き辞書・マッピング作成に便利- 複雑なネストは可読性が下がるため、forループで書いた方がよい場合もある
自由に試してみましょう:
完了するとトップページに進捗が表示されます
