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()