Titanicで学ぶ前処理と特徴量エンジニアリング入門

AI学習を継続中。今回はKaggleの定番教材であるTitanicデータセットを使いながら、
pandasによるデータ分析、欠損値処理、特徴量エンジニアリング、train/test分割まで進めた。

ここ数日はNumPyや統計の基礎を復習していたが、今回から「実際のデータを読む」段階へ入ってきた感覚がある。
ChatGPTを講師兼レビュー役として使いながら進めている。

今回の学習テーマ

  • Titanicデータセットの読み込み
  • 欠損値の確認
  • 統計量の確認
  • 相関分析
  • 特徴量エンジニアリング
  • カテゴリ変数の数値化
  • train_test_split

Titanicデータセットを読み込む

まずはKaggleからTitanicのtrain.csvをダウンロードして読み込んだ。

Kaggle:
https://www.kaggle.com/competitions/titanic

import pandas as pd

pd.set_option("display.max_columns", None)
pd.set_option("display.width", None)

df = pd.read_csv("train.csv")

print(df.head())
print(df.info())
print(df.describe())

pandasの表示が省略される問題

最初、describe()やhead()の出力で列が途中省略されていた。

pandasは列数が多いと自動で省略するため、以下設定を追加すると見やすくなる。

pd.set_option("display.max_columns", None)
pd.set_option("display.width", None)

初学者だと「データが消えた?」と勘違いしやすいポイントだと思う。


欠損値を確認する

print(df.isnull().sum())
print(df.isnull().mean() * 100)

結果を見ると、かなり特徴的だった。

Age       19.8%
Cabin     77.1%
Embarked   0.2%

特にCabinの欠損率が非常に高い。

以前は「欠損値=とりあえず埋める」と考えていたが、
実際には「使うか捨てるか」「別特徴量として扱うか」を考える必要があると学んだ。

ChatGPTとのやり取りの中でも、
「欠損率が高すぎる列は扱いが難しい」
という視点を得られたのが大きかった。


describe()から分布を読む

特に興味深かったのがFare(運賃)列。

mean    32.20
50%     14.45
max    512.32

平均値と中央値が大きく離れている。

最初は「ピークが左側なのに、なぜ右に歪んでいると言うのか?」が分からなかった。

調べながら理解したのは、
「右に歪む(right-skewed)」とは、
右側に長い尾を持つ分布を意味するということ。

つまり、

  • 大半は小さい値
  • 一部だけ極端に大きい値がある

という状態である。

これは実務データでも非常によくあるらしい。

平均と中央値の違い

右に歪んだ分布では、
外れ値によって平均値が引っ張られる。

そのため、

  • 平均より中央値が重要
  • 欠損補完でも中央値を使うことがある

という話がつながって理解できた。


groupby()を使った集計

今回かなり理解が深まったのがgroupby。

print(df.groupby("Sex")["Survived"].mean())

最初は文法がかなり怪しかった。

ただ、ChatGPTとの対話の中で、

  • groupby(“Sex”) → 性別ごとに分割
  • [“Survived”] → 生存列を選択
  • mean() → 平均を計算

という形で理解できた。

Survived列は0/1なので、
平均値=生存率になる。

female    0.742038
male      0.188908

女性の生存率がかなり高い。

ここで「なぜそうなるのか?」という背景まで考えるのが重要らしい。


特徴量エンジニアリングを体験する

今回から、いよいよ「特徴量を作る」作業へ入った。

Kaggleではかなり重要な領域らしい。

FamilySizeを作成

df["FamilySize"] = df["SibSp"] + df["Parch"] + 1

print(df[["SibSp", "Parch", "FamilySize"]].head())

+1しているのは本人を含めるため。

こういう「意味のある特徴量を自分で作る」のが特徴量エンジニアリング。

FamilySizeごとの生存率

print(df.groupby("FamilySize")["Survived"].mean())
FamilySize
1     0.303538
2     0.552795
3     0.578431
4     0.724138
5     0.200000
6     0.136364
7     0.333333
8     0.000000
11    0.000000

ここがかなり面白かった。

  • 一人客は生存率が低い
  • 2〜4人程度は高い
  • 大家族は逆に低い

単なる数値ではなく、
「なぜそうなるのか?」を考えるのが重要らしい。


カテゴリ変数の数値化

機械学習モデルは文字列を扱えないことが多いため、
数値化が必要になる。

df["Sex"] = df["Sex"].map({
    "male": 0,
    "female": 1
})

print(df["Sex"].head())
0    0
1    1
2    1
3    1
4    0

これはEncoding(エンコーディング)と呼ばれる処理。

今後かなり頻出しそう。


train_test_splitで学習データを分割する

今回はまだモデル学習はしていないが、
train_test_splitまで進んだ。

from sklearn.model_selection import train_test_split

features = ["Pclass", "Sex", "Age", "Fare", "FamilySize"]

X = df[features]
y = df["Survived"]

X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(712, 5)
(179, 5)
(712,)
(179,)

Xが大文字、yが小文字なのはなぜ?

ここも疑問だった。

調べると、

  • X → 特徴量行列(複数列)
  • y → 正解ラベル(通常1列)

という慣習らしい。

また、

  • DataFrame → 2次元
  • Series → 1次元

の違いもここで理解できた。


random_state=42とは?

これも気になった。

random_state=42

42に特別な数学的意味はなく、
乱数固定用の値とのこと。

毎回同じ分割結果になるため、
再現性を確保できる。

AIやデータ分析では、
「再現可能性」が非常に重要らしい。


今回学んだこと

  • AIはまずデータを見ることが重要
  • 欠損値処理はかなり重要
  • groupbyはpandasの核心機能
  • 特徴量エンジニアリングがKaggleの本質に近い
  • 平均と中央値の違いを意識する必要がある
  • 分布の歪みや外れ値を見る癖が重要

以前は「AI=モデル作成」というイメージだったが、
実際には前処理やデータ理解の比重がかなり大きいと感じ始めている。

次回はいよいよscikit-learnのLogisticRegressionを使って、
最初の機械学習モデルを作成予定。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA