📚 背景知識(読んでから問題へ)
🚢
Kaggle 入門コンペ「Titanic」は世界中の初学者が最初に挑戦するコンペです。
891行の小規模データで EDA → 前処理 → モデル → 提出 の全工程を体験できます。
最初の目標は「高精度」ではなく 動くパイプラインを作って提出すること です。
💡
ベースラインの CV Accuracy 0.80 前後が出れば合格ライン。ここから特徴量エンジニアリングで 0.83〜0.85 を目指します。
🗂️ データスキーマ
| カラム | 意味 | 型 | 欠損率 | 今回の扱い |
|---|---|---|---|---|
PassengerId |
乗客ID | int | 0% | 除外(IDは情報なし) |
Survived |
生存(1)/死亡(0) ← 目的変数 | int | 0% | y として使用 |
Pclass |
客室クラス(1=1等、3=3等) | int | 0% | 特徴量として使用 |
Name |
氏名(敬称含む) | str | 0% | 今回は除外(次回: 敬称抽出) |
Sex |
性別(male/female) | str | 0% | 0/1 にエンコード |
Age |
年齢 | float | 20%(177件) | 中央値で補完 |
SibSp |
兄弟・配偶者の同乗数 | int | 0% | 特徴量として使用 |
Parch |
親・子どもの同乗数 | int | 0% | 特徴量として使用 |
Ticket |
チケット番号 | str | 0% | 除外(高カーディナリティ) |
Fare |
運賃 | float | 0%(test: 1件) | 特徴量として使用 |
Cabin |
客室番号 | str | 77%(687件) | 除外(欠損多すぎ) |
Embarked |
乗船港(C/Q/S) | str | 0.2%(2件) | 最頻値で補完 → 数値化 |
🔧 パイプライン概要
① データ読み込み
→
② 欠損値補完
→
③ カテゴリ数値化
→
④ StandardScaler
→
⑤ LogisticRegression
→
⑥ Stratified 5-Fold CV
→
⑦ 提出ファイル作成
⚠️
data leakage 防止ポイント: 欠損値補完の統計量(中央値・最頻値)は train のみで計算し、test にも同じ値を適用します。test で計算した値を train に使うのは未来情報のリークになります。
🎯 問題
📋
タスク1: データの前処理
train.csv と test.csv を読み込み、以下の前処理を実施せよ:
・Age の欠損値 → 中央値で補完
・Fare の欠損値(test に1件)→ 中央値で補完
・Embarked の欠損値 → 最頻値で補完
・Sex: male→0, female→1 にエンコード
・Embarked: S→0, C→1, Q→2 にエンコード
train.csv と test.csv を読み込み、以下の前処理を実施せよ:
・Age の欠損値 → 中央値で補完
・Fare の欠損値(test に1件)→ 中央値で補完
・Embarked の欠損値 → 最頻値で補完
・Sex: male→0, female→1 にエンコード
・Embarked: S→0, C→1, Q→2 にエンコード
📋
タスク2: ベースラインモデルの構築
・特徴量: Pclass, Sex, Age, SibSp, Parch, Fare, Embarked
・Pipeline: StandardScaler + LogisticRegression
・Stratified 5-Fold CV で Accuracy を確認する(目標: 0.79以上)
・特徴量: Pclass, Sex, Age, SibSp, Parch, Fare, Embarked
・Pipeline: StandardScaler + LogisticRegression
・Stratified 5-Fold CV で Accuracy を確認する(目標: 0.79以上)
📋
タスク3: 提出ファイルの作成
・test データで予測し、submission.csv を作成する
・フォーマット: PassengerId, Survived(2列のみ、headerあり、indexなし)
・test データで予測し、submission.csv を作成する
・フォーマット: PassengerId, Survived(2列のみ、headerあり、indexなし)
💡 ヒント
ヒント1 — 方向性
方向性のみ
前処理 → 特徴量選択 → モデル学習 → CV評価 → test予測 → 提出ファイルの順に進める。
欠損値の対処を先に終わらせてからモデルに渡す。train の統計量を test にも使う点に注意。
ヒント2 — キー関数
アプローチ
# 欠損補完 age_median = train['Age'].median() df['Age'] = df['Age'].fillna(age_median) # エンコード df['Sex'] = df['Sex'].map({'male': 0, 'female': 1}) # Pipeline pipeline = Pipeline([('scaler', StandardScaler()), ('lr', LogisticRegression())]) # CV skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(pipeline, X, y, cv=skf, scoring='accuracy')
ヒント3 — コード骨格
ほぼ答え
import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') def preprocess(df, age_med=None, embarked_mode=None): df = df.copy() if age_med is None: age_med = df['Age'].median() df['Age'] = df['Age'].fillna(age_med) if embarked_mode is None: embarked_mode = df['Embarked'].mode()[0] df['Embarked'] = df['Embarked'].fillna(embarked_mode) df['Sex'] = df['Sex'].map({'male': 0, 'female': 1}) df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}) return df, age_med, embarked_mode train, a, e = preprocess(train) test, _, _ = preprocess(test, a, e) features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] X, y, X_test = train[features], train['Survived'], test[features] pipeline = Pipeline([('sc', StandardScaler()), ('lr', LogisticRegression(max_iter=1000))]) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(pipeline, X, y, cv=skf, scoring='accuracy') print(scores.mean()) pipeline.fit(X, y) sub = pd.DataFrame({'PassengerId': test['PassengerId'], 'Survived': pipeline.predict(X_test)}) sub.to_csv('submission.csv', index=False)
✅ 模範解答
完全実装コード
実行可能
import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # データ読み込み(Kaggleカーネル内を想定) train = pd.read_csv('/kaggle/input/titanic/train.csv') test = pd.read_csv('/kaggle/input/titanic/test.csv') print(f"Train shape: {train.shape}") print(f"Test shape: {test.shape}") print("\n欠損値確認:") print(train.isnull().sum()[train.isnull().sum() > 0]) # ─── 前処理関数(data leakage 防止) ─── def preprocess(df, age_median=None, fare_median=None, embarked_mode=None): df = df.copy() if age_median is None: age_median = df['Age'].median() df['Age'] = df['Age'].fillna(age_median) if fare_median is None: fare_median = df['Fare'].median() df['Fare'] = df['Fare'].fillna(fare_median) if embarked_mode is None: embarked_mode = df['Embarked'].mode()[0] df['Embarked'] = df['Embarked'].fillna(embarked_mode) df['Sex'] = df['Sex'].map({'male': 0, 'female': 1}) df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}) return df, age_median, fare_median, embarked_mode train, age_med, fare_med, emb_mode = preprocess(train) test, _, _, _ = preprocess(test, age_med, fare_med, emb_mode) # ─── 特徴量 / 目的変数 ─── features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] X = train[features] y = train['Survived'] X_test = test[features] # ─── Pipeline(標準化 + ロジスティック回帰) ─── pipeline = Pipeline([ ('scaler', StandardScaler()), ('lr', LogisticRegression(max_iter=1000, random_state=42)) ]) # ─── Stratified 5-Fold CV ─── skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(pipeline, X, y, cv=skf, scoring='accuracy') print(f"\nCV Accuracy: {scores.mean():.4f} ± {scores.std():.4f}") print(f"各Foldスコア: {np.round(scores, 4)}") # ─── 最終学習 → 提出ファイル作成 ─── pipeline.fit(X, y) preds = pipeline.predict(X_test) submission = pd.DataFrame({ 'PassengerId': test['PassengerId'], 'Survived': preds }) submission.to_csv('submission.csv', index=False) print(f"\nsubmission.csv 作成完了: {len(submission)} 行")
📊
期待される出力:
CV Accuracy: 0.8035 ± 0.0162各Foldスコア: [0.7933 0.8258 0.7989 0.8034 0.7989]🪜 Step-by-Step 解説
1
データ読み込みと欠損確認
train.isnull().sum() で欠損の多い列を特定。Age (177件)・Cabin (687件)・Embarked (2件)。
Cabin は欠損率 77% なので今回は除外。
↓
2
data leakage を防いだ前処理
train で計算した中央値・最頻値を test にも適用する。
train で計算した中央値・最頻値を test にも適用する。
age_median = train['Age'].median() → test にも fillna(age_median)。
test の統計量を train 補完に使ってはいけない(未来情報リーク)。
↓
3
カテゴリ変数のエンコード
df['Sex'].map({'male': 0, 'female': 1}) でラベルエンコード。
male/female に何を割り当てるかは係数の符号が変わるだけで予測精度は同じ。
↓
4
Pipeline で標準化 + モデルをセット
Pipeline にすることで CV 内の各 Fold でも「trainデータだけで scaler をfit」が自動化される。 個別に書くと val データを混入させるリークが起きやすい。
Pipeline([('scaler', StandardScaler()), ('lr', LogisticRegression())])Pipeline にすることで CV 内の各 Fold でも「trainデータだけで scaler をfit」が自動化される。 個別に書くと val データを混入させるリークが起きやすい。
↓
5
Stratified 5-Fold CV でスコア確認
Titanic の生存率は 38%(不均衡)。
Titanic の生存率は 38%(不均衡)。
StratifiedKFold は各 Fold に同じ比率を維持する。
通常の KFold より安定したスコアが得られる。
↓
6
全 train で再学習 → test 予測 → submission.csv 作成
cross_val_score は評価専用。最終モデルは pipeline.fit(X, y) で全データを使って学習し直す。
to_csv('submission.csv', index=False) — index=False 必須。
📈 Titanic Accuracy の目安
※ Accuracy の数値は Public LB 上での目安。CV スコアは多少高く出ることがある。
5-Fold 別スコア詳細
🧮 数学・統計の補足(文系向け)
ロジスティック回帰の直感
📐
「この乗客の Pclass・Sex・Age などから、生存確率を計算する数式」を学習します。
直感: 女性(Sex=1)・1等船室(Pclass=1)は生存確率が高い。学習した重みはそれを反映した数字になる。
確率 = 1 / (1 + exp(-z))(シグモイド関数)で 0〜1 に収める。z = w₁×Pclass + w₂×Sex + w₃×Age + … の重み w を「誰が生き残ったか」から逆算。直感: 女性(Sex=1)・1等船室(Pclass=1)は生存確率が高い。学習した重みはそれを反映した数字になる。
StandardScaler とは
⚖️
各特徴量を「平均0・標準偏差1」に変換する処理。
Fare は 0〜512 の範囲、Age は 0〜80 の範囲。スケールが違うと Fare ばかりが影響する。 StandardScaler で統一することで、全特徴量が公平に扱われる。
変換後の値 = (元の値 - 平均) / 標準偏差Fare は 0〜512 の範囲、Age は 0〜80 の範囲。スケールが違うと Fare ばかりが影響する。 StandardScaler で統一することで、全特徴量が公平に扱われる。
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
test で fit_transform を使う |
「変換しなければ」と思って | test は transform のみ。fit は train 専用 |
CV 後に model.fit 忘れ |
CV 内で fit が終わったと勘違い | CV 後に全 train で改めて fit が必要 |
| Cabin をそのまま使う | 「使えるデータは全部使う」 | 欠損 77% は落とすのが無難(後で戻す) |
| PassengerId を特徴量に含める | データに含まれているから | ID は意味を持たない。必ず除外 |
to_csv で index=True(デフォルト) |
引数を省略 | Kaggle は index 列を受け付けない。index=False 必須 |
| test の統計量で train 補完 | 「まとめて処理したい」 | train の統計量だけで計算し test にも適用(data leakage 防止) |
🚀 次のステップ
-
発展: 特徴量エンジニアリング —
Nameから敬称(Mr./Mrs./Miss.)を抽出して Title 特徴量を作成。SibSp + Parch + 1で FamilySize を作成。これだけで 0.82〜0.83 に改善できる。 -
次回予告 (Day 082): 特徴量エンジニアリング応用 — Titanic の隠れた特徴量を掘り起こす(Title / FamilySize / IsAlone / Deck 等)。
-
実践: Kaggle アカウントを持っていれば今日のコードをノートブックに貼り付けて実際に提出してみよう。0.77〜0.78 あたりが Public LB の目安。