Day 042 — pandas入門(loc・iloc・apply)

2026-05-22 白 / Phase 1 理論 pandas入門

📚 背景知識(読んでから問題へ)

loc vs iloc — 名前か番号か

df.loc[ ]

名前(ラベル) で指定
行 index 値・列名を使う
条件フィルタリングにも対応

df.iloc[ ]

番号(整数) で指定
0 始まりの位置で指定
Python スライスと同じ感覚

記法行の指定列の指定条件フィルタ
loc index 値(例: 0, "Alice") 列名(例: "Age") 可能(最重要用途)
iloc 整数位置(例: 0, 1, 2) 整数位置(例: 2, 3) 不可

apply — 関数を全行に適用する

apply() は「自分で書いた処理を DataFrame の各要素に一括適用する」命令です。Excel の「フィル」機能を Python 関数で自動化するイメージです。

元の列
Age列
[22,35,28,45]
apply(関数)
age_category()
を各行に適用
新しい列
["Young","Senior",
"Middle","Senior"]

🗂️ データスキーマ

架空のタイタニック号風データ(乗客7名分)を使用します。

列名列番号(iloc用)説明値の範囲
Name object 0 乗客名
Sex object 1 性別 "F" / "M"
Pclass int64 2 チケットクラス 1 / 2 / 3
Age int64 3 年齢 19 〜 45
Fare float64 4 運賃 7.25 〜 71.28
Survived int64 5 生存フラグ 0 / 1
index Name
[0]
Sex
[1]
Pclass
[2]
Age
[3]
Fare
[4]
Survived
[5]
0AliceF12271.281
1BobM3357.250
2CarolF22813.001
3DaveM3458.050
4EveF11971.001
5FrankM23021.000
6GraceF22613.501

緑背景 = loc[Survived==1, ["Name","Fare"]] で抽出される行・列 | 青背景 = iloc[0:3, 2:5] で抽出される範囲 | 緑左ボーダー = 生存者行

📝 問題

📌
シナリオ: loc / iloc / apply を使ったデータ操作を4問実装してください。
import pandas as pd
import numpy as np

data = {
    "Name":     ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank", "Grace"],
    "Sex":      ["F", "M", "F", "M", "F", "M", "F"],
    "Pclass":   [1, 3, 2, 3, 1, 2, 2],
    "Age":      [22, 35, 28, 45, 19, 30, 26],
    "Fare":     [71.28, 7.25, 13.00, 8.05, 71.00, 21.00, 13.50],
    "Survived": [1, 0, 1, 0, 1, 0, 1],
}
df = pd.DataFrame(data)

問1 — loc で条件フィルタリング

df.loc[] を使って「生存者(Survived == 1)の Name と Fare 列のみ」を抽出してください。

💡
期待する出力: Name と Fare の2列、生存者4名分(Alice / Carol / Eve / Grace)

問2 — iloc で特定範囲を取得

df.iloc[] を使って「0〜2行目(Alice, Bob, Carol)の2〜4列目(Pclass, Age, Fare)」を取得してください。

💡
期待する出力: 3行 × 3列(Pclass / Age / Fare)の DataFrame

問3 — apply で新しい列を作る

apply() を使って「年齢カテゴリ」列 AgeGroup を追加してください。

条件
Age < 25"Young"
25 ≤ Age < 35"Middle"
Age ≥ 35"Senior"

追加後、NameAgeAgeGroup の3列を表示してください。

問4 — loc で値を更新する

df.loc[条件, 列名] = 新しい値 構文を使って、Pclass が 3(三等客)の Fare を全員 10.0 に更新してください。

💡
Pclass=3 は Bob(7.25)と Dave(8.05)の2名。更新後は両者とも 10.0 になります。

🔍 ヒント(段階的開示)

ヒント1 — 方向性
  • 問1: df.loc[条件, 列リスト] — 条件と列を同時に指定できる
  • 問2: df.iloc[行スライス, 列スライス] — 数値で範囲指定
  • 問3: df["Age"].apply(関数) — if/elif/else を関数で表現
  • 問4: df.loc[df["Pclass"] == 3, "Fare"] = 10.0 — 条件付き代入
ヒント2 — アプローチ
# 問1
df.loc[df["Survived"] == 1, ["Name", "Fare"]]

# 問2
df.iloc[0:3, 2:5]

# 問3
def age_category(age):
    if age < 25: return "Young"
    elif age < 35: return "Middle"
    else: return "Senior"
df["AgeGroup"] = df["Age"].apply(age_category)

# 問4
df.loc[df["Pclass"] == 3, "Fare"] = 10.0
ヒント3 — コード骨格(ほぼ答え)
# 問1
survivors = df.loc[df[___] == ___, [___, ___]]

# 問2
subset = df.iloc[___:___, ___:___]

# 問3
df["AgeGroup"] = df[___].apply(
    lambda x: "Young" if x < ___ else ("Middle" if x < ___ else ___)
)

# 問4
df.loc[df[___] == ___, ___] = ___

模範解答

import pandas as pd
import numpy as np

data = {
    "Name":     ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank", "Grace"],
    "Sex":      ["F", "M", "F", "M", "F", "M", "F"],
    "Pclass":   [1, 3, 2, 3, 1, 2, 2],
    "Age":      [22, 35, 28, 45, 19, 30, 26],
    "Fare":     [71.28, 7.25, 13.00, 8.05, 71.00, 21.00, 13.50],
    "Survived": [1, 0, 1, 0, 1, 0, 1],
}
df = pd.DataFrame(data)

# 問1: loc で条件フィルタリング
survivors = df.loc[df["Survived"] == 1, ["Name", "Fare"]]
print("問1: 生存者のName・Fare")
print(survivors)
#     Name    Fare
# 0  Alice  71.28
# 2  Carol  13.00
# 4    Eve  71.00
# 6  Grace  13.50

# 問2: iloc で特定範囲
subset = df.iloc[0:3, 2:5]
print("\n問2: iloc[0:3, 2:5]")
print(subset)
#    Pclass  Age   Fare
# 0       1   22  71.28
# 1       3   35   7.25
# 2       2   28  13.00

# 問3: apply で年齢カテゴリ
def age_category(age):
    if age < 25:
        return "Young"
    elif age < 35:
        return "Middle"
    else:
        return "Senior"

df["AgeGroup"] = df["Age"].apply(age_category)
print("\n問3: AgeGroup列を追加")
print(df[["Name", "Age", "AgeGroup"]])

# 問4: loc で値を更新
print("\n問4: 更新前のFare")
print(df[["Name", "Pclass", "Fare"]])
df.loc[df["Pclass"] == 3, "Fare"] = 10.0
print("\n更新後のFare(Pclass=3 → 10.0)")
print(df[["Name", "Pclass", "Fare"]])

問3 — AgeGroup の出力結果

indexNameAgeAgeGroup
0Alice22Young
1Bob35Senior
2Carol28Middle
3Dave45Senior
4Eve19Young
5Frank30Middle
6Grace26Middle

問4 — Fare 更新の前後比較

NamePclassFare(更新前)Fare(更新後)
Alice171.2871.28
Bob37.2510.0
Carol213.0013.00
Dave38.0510.0
Eve171.0071.00
Frank221.0021.00
Grace213.5013.50

青文字 = 更新された値(Pclass=3 の Bob と Dave)

🪜 Step-by-Step 解説

Step 1: loc の「条件フィルタ + 列選択」パターン

df.loc[df["Survived"] == 1, ["Name", "Fare"]]

df["Survived"] == 1 は True/False の Series を返します。loc[] はその True 行だけを取り出し、同時に ["Name", "Fare"] で列を絞り込みます。

💡
列を1つだけ選ぶ場合は文字列 "Fare"、複数選ぶ場合はリスト ["Name", "Fare"] を渡す点に注意してください。

Step 2: iloc のスライスは Python リストと同じ「終端を含まない」

df.iloc[0:3, 2:5]
  • 行: 0:3 → 0行目・1行目・2行目(3行目は含まない)
  • 列: 2:5 → 2列目(Pclass)・3列目(Age)・4列目(Fare)(5列目は含まない)

Step 3: apply の名前付き関数 vs lambda

# 名前付き関数(読みやすい)
def age_category(age):
    if age < 25: return "Young"
    elif age < 35: return "Middle"
    else: return "Senior"
df["AgeGroup"] = df["Age"].apply(age_category)

# lambda(1行で書く場合)
df["AgeGroup"] = df["Age"].apply(
    lambda x: "Young" if x < 25 else ("Middle" if x < 35 else "Senior")
)

複雑な条件は名前付き関数のほうが読みやすく、デバッグも容易です。

Step 4: チェーンインデックス問題(重要)

# NG: SettingWithCopyWarning が出る可能性がある
df[df["Pclass"] == 3]["Fare"] = 10.0

# OK: loc で一発指定する
df.loc[df["Pclass"] == 3, "Fare"] = 10.0
⚠️
NG パターンはコピーに代入している可能性があり、元の DataFrame が更新されない場合があります。値を更新するときは 必ず loc を使う のが鉄則です。

📊 loc vs iloc — スライス範囲の違い

loc[0:2] vs iloc[0:2] — どこまで含むか? loc[0:2](終端を含む) 0 1 2 3 0, 1, 2 の3行(2を含む) iloc[0:2](終端を含まない) 0 1 2 0, 1 の2行(2を含まない) iloc[0:3] で3行取得したい場合は終端を +1 する loc[0:2] → 3行 (0, 1, 2 を含む) = iloc[0:3] → 3行 (0, 1, 2 を含む)

⚙️ apply() の動作フロー

df["Age"].apply(age_category) の実行イメージ Age列 22 35 28 ... 関数を適用 age_category(x) x < 25 → Young x < 35 → Middle AgeGroup列 Young Senior Middle ...

🔢 数学・統計の補足(文系向け)

loc と iloc の違いを「番地」で理解する

DataFrame の index は「番地」、iloc の位置は「何番目か」です。

例: 宿泊名簿(index が部屋番号)
loc[203, "Name"] → 203号室の名前を見る(部屋番号で指定)
iloc[2, 0] → 3番目の部屋の最初の情報を見る(位置で指定)
index がデフォルト(0,1,2...)の場合は一致するが、index が部屋番号(101,102,203...)になったとたん loc と iloc の結果が変わる

apply() は「写像(関数)」の概念

数学の「f(x)」と同じです。各行の値 x を関数 f に通して新しい値を得ます。

f(22) → "Young"
f(35) → "Senior"
f(28) → "Middle"

🏆 Kaggleでの実践的な使い方

操作Kaggle場面具体例
loc[条件, 列] 欠損値の確認・対処 Age が欠損している行だけ絞り込んで確認
loc[条件, 列] = 値 条件付き欠損値補完 男性乗客の Age 欠損を男性平均で埋める
iloc[0:5] 先頭データの確認 df.head() と同じ。特定列だけ同時に絞れる利点あり
apply() 特徴量エンジニアリング Family Size = SibSp + Parch + 1 の新列作成

Kaggleでの実践コード例

# 欠損値の条件付き補完(Titanic の典型的パターン)
male_mean_age = df.loc[df["Sex"] == "male", "Age"].mean()
female_mean_age = df.loc[df["Sex"] == "female", "Age"].mean()

df.loc[(df["Sex"] == "male") & (df["Age"].isna()), "Age"] = male_mean_age
df.loc[(df["Sex"] == "female") & (df["Age"].isna()), "Age"] = female_mean_age

# apply で Family Size 特徴量を作る
df["FamilySize"] = df.apply(
    lambda row: row["SibSp"] + row["Parch"] + 1, axis=1
)

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
lociloc を混在させる 名前か番号か意識しない loc は名前(ラベル)、iloc は番号(位置)。index をカスタムした場合は特に違いが重要
iloc[0:3] が 4 行返ると思う loc のスライスと混同 iloc は Python 式で終端を含まない(0, 1, 2 の3行)。loc[0:3] は 0, 1, 2, 3 の4行
df[条件]["列"] = 値 で代入 チェーンインデックスの誤用 df.loc[条件, "列"] = 値 で必ず一発指定する(SettingWithCopyWarning を避ける)
apply(関数名()) と括弧をつける 関数を「呼び出して」渡してしまう apply(関数名) — 括弧なしで「関数オブジェクト」を渡す
apply() を使いすぎる 汎用性があるから何でも使う ベクトル化操作(+, *, .str など)のほうが高速。apply() は複雑な条件が必要なときに限定する

🚀 次のステップ

  • 発展: df.apply(func, axis=1) で複数列を組み合わせた特徴量エンジニアリング(FamilySize = SibSp + Parch + 1)
  • 次回予告: pandas 続編 — データの読み込みと基本確認(shapedtypesdescribeinfo

📋 自己評価(解いた後に記入)

自分の回答・気づき・メモ: