Kaggle:タイタニック号乗客の生存予測のデータセット その18:Ageの欠損値を修正してみる Name注目編3

目次

Kaggle

前回は機械学習・データサイエンスのプラットフォーム「Kaggle(カグル)」の「タイタニック号乗客の生存予測」のデータセットのNameの敬称の年齢分布を表示してみました。

今回は敬称の解析結果をもとにAgeの欠損値を修正し、機械学習・予想を行い、Kaggleにデータを提出してみましょう。

ということでまずはデータの読み込みとこれまでの修正を行っていきます。

<セル1>

import pandas as pd

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

train.loc[train["Sex"] == "male", "Sex"] = 0
train.loc[train["Sex"] == "female", "Sex"] = 1
test.loc[test["Sex"] == "male", "Sex"] = 0
test.loc[test["Sex"] == "female", "Sex"] = 1

test.loc[test["Fare"].isnull() == True, "Fare"] = 7.8875

train.loc[train["Embarked"].isnull() == True, "Embarked"] = "S"

train.loc[train["Embarked"] == "S", "Embarked"] = 0
train.loc[train["Embarked"] == "C", "Embarked"] = 1
train.loc[train["Embarked"] == "Q", "Embarked"] = 2
test.loc[test["Embarked"] == "S", "Embarked"] = 0
test.loc[test["Embarked"] == "C", "Embarked"] = 1
test.loc[test["Embarked"] == "Q", "Embarked"] = 2

all_data = pd.concat([train.drop(columns = "Survived"), test])

all_data

実行結果

それでは始めていきましょう。

敬称を解析し、数値に置き換える

まずはNameの中にある敬称を解析し、数値データに置き換えます。

とは言ってもここは前回プログラミングしているので、前回のまま使っていきましょう。

<セル2>

title_list = []

for name in all_data["Name"]:
    for name_split in name.split():
        if name_split.endswith(".") == True:
            if name_split != "L.":
                title_list.append(name_split)

title_unique = set(title_list)      

print(title_unique)

実行結果
{'Sir.', 'Don.', 'Ms.', 'Capt.', 'Dona.', 'Miss.', 'Major.', 'Mrs.', 'Col.', 'Jonkheer.', 'Rev.', 'Mme.', 'Master.', 'Lady.', 'Countess.', 'Dr.', 'Mlle.', 'Mr.'}
<セル3>

title_num = []

for title in title_list:
    if title == "Mr.":
        title_num.append(0)
    elif title == "Miss.":
        title_num.append(1)
    elif title == "Mrs.":
        title_num.append(2)
    elif title == "Master.":
        title_num.append(3)
    else:
        title_num.append(4)
        
all_data["title"] = title_num

all_data

実行結果

これで全データの敬称を数字に置き換え、新たな列として追加できました。

Ageの欠損値を修正する

ここから訓練用データセット、テスト用データセットのAgeの欠損値を修正していきます。

基本的にはこちらの記事でやったように重みありのランダム値を使って修正していきます。

ただし今回は敬称において、別の重み(年齢分布)を使っていくため、少し難易度が上がっています。

まずはAgeの欠損値を敬称別の年齢分布を重みとして修正するプログラム全体を見てみましょう。

<セル4>

import numpy as np
import random

titleno = [0, 1, 2, 3, 4]

for t_num in titleno:
    age_values = all_data[all_data["title"] == t_num]["Age"].value_counts()
    age_values = age_values.sort_index()
    age_values = np.array(age_values)

    age_list = all_data[all_data["title"] == t_num]["Age"].unique()
    age_list = np.sort(age_list)

    random_age_list = random.choices(age_list[:-1], k = len(all_data), weights = age_values)
    random_age_list = np.array(random_age_list)

    random_list_no = 0

    for i in range(len(train)):
        if np.isnan(train.iloc[i, 5]) == True:
            if train.iloc[i, 11] == t_num:
                train.iloc[i, 5] = random_age_list[random_list_no]
                random_list_no = random_list_no + 1

    for i in range(len(test)):
        if np.isnan(test.iloc[i, 4]) == True:
            if test.iloc[i, 10] == t_num:
                test.iloc[i, 4] = random_age_list[random_list_no]
                random_list_no = random_list_no + 1

実行結果

では上から順に見ていきましょう。

最初は「numpy」と「random」のインポート

import numpy as np
import random

次に各敬称ごとに「重み」に使用する年齢データを作成していきます。

titleno = [0, 1, 2, 3, 4]

for t_num in titleno:
    age_values = all_data[all_data["title"] == t_num]["Age"].value_counts()
    age_values = age_values.sort_index()
    age_values = np.array(age_values)

    age_list = all_data[all_data["title"] == t_num]["Age"].unique()
    age_list = np.sort(age_list)

「titleno = [0, 1, 2, 3, 4]」として、titleで出現する5つの数字のリストを作成し、「for t_num in titleno:」とすることで、その数字を順番に「t_num」に格納していきます。

そして「age_values = all_data[all_data[“title”] == t_num][“Age”].value_counts()」で「t_num」とTitleの列の数字が一致したデータのみを取りつつ、それぞれの値に対する個数を取得します。

「age_values = age_values.sort_index()」で年齢順に並び替え、「age_values = np.array(age_values)」でnumpyの配列に変換します。

次に「age_list = all_data[all_data[“title”] == t_num][“Age”].unique()」で「t_num」とTitleの列の数字が一致したデータのみを取りつつ、年齢のリストを作成します。

そして「age_list = np.sort(age_list)」で年齢順に並び替えます。

次にランダム値のリストを作成します。

    random_age_list = random.choices(age_list[:-1], k = len(all_data), weights = age_values)
    random_age_list = np.array(random_age_list)

作成したランダム値を訓練用データセットとテスト用データセットのAgeの欠損値に置き換えていきます。

    random_list_no = 0

    for i in range(len(train)):
        if np.isnan(train.iloc[i, 5]) == True:
            if train.iloc[i, 11] == t_num:
                train.iloc[i, 5] = random_age_list[random_list_no]
                random_list_no = random_list_no + 1

    for i in range(len(test)):
        if np.isnan(test.iloc[i, 4]) == True:
            if test.iloc[i, 10] == t_num:
                test.iloc[i, 4] = random_age_list[random_list_no]
                random_list_no = random_list_no + 1

ちなみに前回と比較すると「if train.iloc[i, 11] == t_num:」もしくは「if test.iloc[i, 10] == t_num:」という条件分岐が入っており、ここでTitleの選別をしています。

これでテスト用データセットと訓練用データセットのAgeの欠損値が修正されているはずです。

ということで確認してみましょう。

<セル5>

train.isnull().sum()

実行結果

PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age              0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         0
dtype: int64
<セル6>

test.isnull().sum()

PassengerId      0
Pclass           0
Name             0
Sex              0
Age              0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          327
Embarked         0
dtype: int64

それぞれAgeの欠損値の個数が「0」になっており、修正されていることが確認できました。

機械学習・予測 その1

ということで機械学習・予測、そしてKaggleへの提出をしてみましょう。

<セル7>

from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

x = train.loc[:, ["Pclass", "Sex", "SibSp", "Parch", "Fare", "Embarked", "Age"]]
y = train["Survived"]

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, train_size=0.8)

model = LinearSVC(max_iter=10000000)
model.fit(x_train, y_train)
pred = model.predict(x_test)

print(accuracy_score(y_test, pred))

実行結果
0.8268156424581006

これまでのここでのスコアはこんな感じです。

修正項目機械学習スコア
初期(修正なし)0.79330
Fare0.80447
Embarked0.83240
Age(平均値)0.81564
Age(ランダム値)0.74860
Age(重みありランダム値)0.77095

修正しているとは言え、なかなか綺麗に上がっていかないものですね。

<セル8>

x_testset = test.loc[:, ["Pclass", "Sex", "SibSp", "Parch", "Fare", "Embarked", "Age"]]

pred = model.predict(x_testset)

submit_data = pd.DataFrame()

submit_data["PassengerId"] = test["PassengerId"]
submit_data["Survived"] = pred

submit_data = submit_data.set_index("PassengerId")

submit_data.to_csv("./submit_data_Age_title_1.csv")

これでKaggleに提出してみたところ、これまでの最高値「0.77990」は越えられませんでした。

上の図では最高値が「0.78229」になっていますが、これは色々試していたら出てしまった値なので、ちょっと見なかったことにしてください…

ただ今回はもう一つ策があるので試してみましょう。

機械学習・予測 その2

もう一つの策とは「Title」の情報も機械学習のための特徴量として使用してしまうということです。

例えばDr.やSir. など社会的立場の高い人たちは先に脱出ボートに乗ることができたという可能性も考えられ、生存に影響するかもしれません。

ということでまずはTitleの情報を訓練用データセット、テスト用データセットに付け加えていきます。

<セル9>

title_list_train = []

for name in train["Name"]:
    for name_split in name.split():
        if name_split.endswith(".") == True:
            if name_split != "L.":
                title_list_train.append(name_split)

title_num_train = []

for title in title_list_train:
    if title == "Mr.":
        title_num_train.append(0)
    elif title == "Miss.":
        title_num_train.append(1)
    elif title == "Mrs.":
        title_num_train.append(2)
    elif title == "Master.":
        title_num_train.append(3)
    else:
        title_num_train.append(4)
        
train["title"] = title_num_train
<セル10>

title_list_test = []

for name in test["Name"]:
    for name_split in name.split():
        if name_split.endswith(".") == True:
            if name_split != "L.":
                title_list_test.append(name_split)

title_num_test = []

for title in title_list_test:
    if title == "Mr.":
        title_num_test.append(0)
    elif title == "Miss.":
        title_num_test.append(1)
    elif title == "Mrs.":
        title_num_test.append(2)
    elif title == "Master.":
        title_num_test.append(3)
    else:
        title_num_test.append(4)
        
test["title"] = title_num_test

それでは機械学習・予想をしていきましょう。

<セル11>

from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

x = train.loc[:, ["Pclass", "Sex", "SibSp", "Parch", "Fare", "Embarked", "Age", "title"]]
y = train["Survived"]

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, train_size=0.8)

model = LinearSVC(max_iter=10000000)
model.fit(x_train, y_train)
pred = model.predict(x_test)

print(accuracy_score(y_test, pred))

実行結果
0.8100558659217877

これまでのデータと比較するとこんな感じです。

修正項目機械学習スコア
初期(修正なし)0.79330
Fare0.80447
Embarked0.83240
Age(平均値)0.81564
Age(ランダム値)0.74860
Age(重みありランダム値)0.77095
Age(敬称による重みありランダム値)0.82682
<セル12>

x_testset = test.loc[:, ["Pclass", "Sex", "SibSp", "Parch", "Fare", "Embarked", "Age", "title"]]

pred = model.predict(x_testset)

submit_data = pd.DataFrame()

submit_data["PassengerId"] = test["PassengerId"]
submit_data["Survived"] = pred

submit_data = submit_data.set_index("PassengerId")

submit_data.to_csv("./submit_data_Age_title_2.csv")

それではデータを提出してみましょう。

これまでの最高値「0.77990」を越え「0.78468」になりました。

でもここまで修正してきてもなかなか8割の大台には乗ってきませんね。

なかなか手詰まりになってきたので、Kaggleに関しては一旦ここで終わりとしたいと思います。

また興味が向いたら、挑戦してみることにしましょう。

次回からはTwitterをPythonで制御できるようTwitter APIをいじっていきます。

ということで今回はこんな感じで。

よかったらシェアしてね!

コメント

コメントする

目次
閉じる