📚 背景知識(読んでから問題へ)
loc vs iloc — 名前か番号か
名前(ラベル) で指定
行 index 値・列名を使う
条件フィルタリングにも対応
番号(整数) で指定
0 始まりの位置で指定
Python スライスと同じ感覚
| 記法 | 行の指定 | 列の指定 | 条件フィルタ |
|---|---|---|---|
loc |
index 値(例: 0, "Alice") | 列名(例: "Age") | 可能(最重要用途) |
iloc |
整数位置(例: 0, 1, 2) | 整数位置(例: 2, 3) | 不可 |
apply — 関数を全行に適用する
apply() は「自分で書いた処理を DataFrame の各要素に一括適用する」命令です。Excel の「フィル」機能を Python 関数で自動化するイメージです。
[22,35,28,45]
を各行に適用
"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] |
|---|---|---|---|---|---|---|
| 0 | Alice | F | 1 | 22 | 71.28 | 1 |
| 1 | Bob | M | 3 | 35 | 7.25 | 0 |
| 2 | Carol | F | 2 | 28 | 13.00 | 1 |
| 3 | Dave | M | 3 | 45 | 8.05 | 0 |
| 4 | Eve | F | 1 | 19 | 71.00 | 1 |
| 5 | Frank | M | 2 | 30 | 21.00 | 0 |
| 6 | Grace | F | 2 | 26 | 13.50 | 1 |
緑背景 = loc[Survived==1, ["Name","Fare"]] で抽出される行・列 |
青背景 = iloc[0:3, 2:5] で抽出される範囲 |
緑左ボーダー = 生存者行
📝 問題
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 列のみ」を抽出してください。
問2 — iloc で特定範囲を取得
df.iloc[] を使って「0〜2行目(Alice, Bob, Carol)の2〜4列目(Pclass, Age, Fare)」を取得してください。
問3 — apply で新しい列を作る
apply() を使って「年齢カテゴリ」列 AgeGroup を追加してください。
| 条件 | 値 |
|---|---|
| Age < 25 | "Young" |
| 25 ≤ Age < 35 | "Middle" |
| Age ≥ 35 | "Senior" |
追加後、Name・Age・AgeGroup の3列を表示してください。
問4 — loc で値を更新する
df.loc[条件, 列名] = 新しい値 構文を使って、Pclass が 3(三等客)の Fare を全員 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 の出力結果
| index | Name | Age | AgeGroup |
|---|---|---|---|
| 0 | Alice | 22 | Young |
| 1 | Bob | 35 | Senior |
| 2 | Carol | 28 | Middle |
| 3 | Dave | 45 | Senior |
| 4 | Eve | 19 | Young |
| 5 | Frank | 30 | Middle |
| 6 | Grace | 26 | Middle |
問4 — Fare 更新の前後比較
| Name | Pclass | Fare(更新前) | Fare(更新後) |
|---|---|---|---|
| Alice | 1 | 71.28 | 71.28 |
| Bob | 3 | 7.25 | 10.0 |
| Carol | 2 | 13.00 | 13.00 |
| Dave | 3 | 8.05 | 10.0 |
| Eve | 1 | 71.00 | 71.00 |
| Frank | 2 | 21.00 | 21.00 |
| Grace | 2 | 13.50 | 13.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"] で列を絞り込みます。
"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
loc を使う のが鉄則です。📊 loc vs iloc — スライス範囲の違い
⚙️ apply() の動作フロー
🔢 数学・統計の補足(文系向け)
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
)
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
loc と iloc を混在させる |
名前か番号か意識しない | 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 続編 — データの読み込みと基本確認(
shape・dtypes・describe・info)