コンテンツにスキップ

8 機械学習の実際

8.1 テーマ

テーマは豪華客船タイタニック号です。乗客名簿から、どのような特徴を持つ乗客が生き残れたのかを分析します。

まずデータを見てみましょう。 ファイル titanic.ipynb に以下を記述します。

import pandas as pd
df = pd.read_csv("titanic.csv")
df.head()

以下のようなデータです。

  • 生存 1・・生存 0・・死亡
  • 客室 1~3等客室
  • 性別 male:男性 female 女性
  • 年齢
  • 兄弟配偶者 乗船した兄弟/配偶者の人数
  • 親子 乗船した親子の人数
  • 料金
  • 客室番号
  • 出発地 C=Cherbourg,Q=Queenstown,S=Southampton

8.2 前処理

まず、欠損値がある列があるかを確認します。

# 欠損の確認
df.isnull().sum()

年齢、客室番号、出発地に欠損データがあるようです。 まず年齢を平均値で埋めましょう。

# 年齢を平均値で埋める
heikin = df["年齢"].mean()
df["年齢"] = df["年齢"].fillna(heikin)
df.isnull().sum()

客室番号は欠損地が非常に多いため今回は削除します。また、氏名とチケット番号もばらばらな文字列が入っているため、今回は削除します。

# 不要列の削除
df = df.drop(["客室番号","氏名","チケット番号"],axis=1)
df.head()

出発地が無いデータは2件しか無いため、今回はその2件を削除します。

# 出発地がない行の削除
df = df.dropna(subset=["出発地"])
df.isnull().sum()

また、性別データ、出発地は種別(カテゴリ)を表す文字列です。このようなデータは数値に変換します。このとき、0と1のみになるように変換します。

例えば、性別であれば、maleとfemaleがあります。ですので、性別_maleと性別_femaleという列を作り、該当するところを1に、それ以外を0にします。

性別 性別_male 性別_female
male 1 0
female 0 1

出発地であれば、CとQとSがあるので、以下のように変換します。

出発地 出発地_C 出発地_Q 出発地_S
C 1 0 0
Q 0 1 0
S 0 0 1

このように値の列を作り、それに該当する場所だけが1になるように変換する処理をワンホットエンコーディングといいます。

なお、性別のデータの例で言えば性別_male と 性別_female のどちらかだけでかまいません。なぜならどちらが0ならどちらが1だからです。このように通常は、最初の列を削除します。

これを以下で行います。最初に変数 catsに変換したい列名を入れます。最後にdf2に結果がでます。

# カテゴリ列の指定
cats = ['性別','出発地']

# ワンホットエンコーディング
df2 = pd.get_dummies(df, columns=cats, drop_first=True)
df2.head()

8.3 モデルの構築と予測

学習データとしてどの列を使うかを決めます。 今回は正解ラベルは「生存」列、特徴量は「生存」以外の全ての列を選択してみました。

# 特徴量
x = df2.drop(['生存'], axis=1) # 生存以外の全列

# 正解ラベル
y = df2["生存"]

データを学習用とテスト用に分けて、今回はランダムフォレストで学習します。 テスト用データを全て予測し、結果を見てみましょう。

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state=0)

from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=0)

model.fit(x_train, y_train)
model.score(x_test, y_test)

8.4 重要度の分析

この時点で何が生存に影響を与えたのかを探ることが出来ます。 決定木の場合、model.feature_importances_ で特徴量の影響度が分かるのでした。

特徴量の列名と組み合わせて降順で表示してみましょう。

# 特徴量の影響度
result = pd.Series(model.feature_importances_, x_train.columns)
result.sort_values(ascending=False)

グラフで表示してみましょう。

import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(font=["Meiryo"])

# 特徴量の影響度グラフ
result.sort_values().plot.barh()

性別が大きな影響を与えたと分かります。

性別ごとに生存者数を棒グラフに出してみましょう。seabornのcountplotで可能です。

sns.countplot(x='性別', hue='生存', data=df)
plt.title('性別ごとの生存人数')
plt.show()

また、年齢も大きな影響を与えています。年齢層毎に区切った「年齢層」という列を新たに作り、「年齢層」ごとに生存の平均を出してみましょう。

# 「年齢層」ごとの生存率
bins = [0, 10, 20, 30, 40, 50, 60, 100]
df["年齢層"] = pd.cut(df["年齢"], bins=bins, right=False)
df.groupby("年齢層")["生存"].mean()