ギークなエンジニアを目指す男

機械学習系の知識を蓄えようとするブログ

Pythonで決定木とRandomForestを比較してみる。その1(タイタニック生存者予測)

今回は、Kaggle初心者向けに公開されているデータセットを使ってタイタニックの生存者予測」Pythonを使って行います。

データセットは下記Kaggleサイトからダウンロードをお願いします。 https://www.kaggle.com/c/titanic/data

データを傍観してみる

とりあえず必要なライブラリをimportして、データを見てみます。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

# CSVデータの読み込み
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

# データ確認
train.head()

f:id:taxa_program:20180518231858p:plain

# テストデータも確認してみる
test.head()

f:id:taxa_program:20180518232000p:plain

ちなみに各項目はこんな感じです。

  • PassengerId – 乗客識別ユニークID
  • Survived – 生存フラグ(0=死亡、1=生存)
  • Pclass – チケットクラス
  • Name – 乗客の名前
  • Sex – 性別(male=男性、female=女性)
  • Age – 年齢
  • SibSp – タイタニックに同乗している兄弟/配偶者の数
  • parch – タイタニックに同乗している親/子供の数
  • ticket – チケット番号
  • fare – 料金
  • cabin – 客室番号
  • Embarked – 出港地(タイタニックへ乗った港)

試しに、男女の生存者を比較してみましょう。なんとなくですが、女性の方が生き残っていたような記憶があります。

# プロット
sns.countplot('Sex',hue='Survived',data=train)

f:id:taxa_program:20180518232258p:plain

やはり女性の方が生存者が多いようです。

統計情報もみておきましょう。

train.describe()

f:id:taxa_program:20180518232452p:plain

数値データでは、Ageのデータが少し欠損していることがわかります。
ぜひ、testデータの統計情報も確認して見てください。

データセットの欠損データチェック

提供されている(または使う)データセットで100%データが揃っていることは珍しいようです。どこかのデータが欠損してたり、信用性が低いため使えなかったりする場合がほとんどみたい。

前のセクションで、Ageデータが欠損していることはわかりましたが、数値項目以外もまとめてみてみます。

## ataframeの欠損データをisnull()で探して、カラム毎に返す関数
def kesson_table(df): 
        null_val = df.isnull().sum()
        percent = 100 * df.isnull().sum()/len(df)
        kesson_table = pd.concat([null_val, percent], axis=1)
        kesson_table_ren_columns = kesson_table.rename(
        columns = {0 : '欠損数', 1 : '%'})
        return kesson_table_ren_columns
 
kesson_table(train)
kesson_table(test)

f:id:taxa_program:20180518233006p:plain f:id:taxa_program:20180518233020p:plain

左がtrainで右がtestです。「Age」と「Cabin」の2つの項目で欠損が多いことがわかります。

データセットの事前処理

欠損データを放っておくのはあまりよろしくないので、欠損データを代理データに入れ替えます。さらに、文字列カテゴリカルデータを数字へ変換していきます。
今回はCabinの値は使用しないので、その他の欠損値を代理データに入れ替えていきます。

まず「Fare」「Age」ですが、シンプルに train の全データの中央値(Median)を代理として使いましょう。(代理データで何を使うか、どのような処理を加えるかは非常に重要かつ大きな議論ではありますが、ここはシンプルに考えて進めます)

次に「Embarked」(出港地)ですが、こちらも2つだけ欠損データが train に含まれています。他のデータを確認すると「S」が一番多い値でしたので、代理データとして「S」を使いましょう。

#欠損値処理
train['Fare'] = train['Fare'].fillna(train['Fare'].median())
train['Age'] = train['Age'].fillna(train['Age'].median())
train['Embarked'] = train['Embarked'].fillna('S')

# 欠損値の確認
kesson_table(train)

Cabon以外の欠損値がなくなったことがわかるかと思います。 f:id:taxa_program:20180518234636p:plain

次はカテゴリカルデータの文字列を数字に変換しましょう。今回の予想で使う項目で文字列を値として持っているカラムは「Sex」と「Embarked」の2種類となります。Sexは「male」「female」の2つのカテゴリー文字列、Embarkedはは「S」「C」「Q」の3つの文字列となります。これらを数字に変換しましょう。

#カテゴリ変数の変換
train['Sex'] = train['Sex'].apply(lambda x: 1 if x == 'male' else 0)
train['Embarked'] = train['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)
# データを表示してみる
train.head(10)

数字に置換されました。 f:id:taxa_program:20180518234922p:plain

testに関しても同様の処理を行います。

#欠損値処理
test['Fare'] = test['Fare'].fillna(test['Fare'].median())
test['Age'] = test['Age'].fillna(test['Age'].median())
test['Embarked'] = test['Embarked'].fillna('S')
#カテゴリ変数の変換
test['Sex'] = test['Sex'].apply(lambda x: 1 if x == 'male' else 0)
test['Embarked'] = test['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)

決定木

scikit-learnから決定木をimportし、モデルを作成していきます。

from sklearn.tree import DecisionTreeClassifier

# トレーニングデータの分割
target = train["Survived"].values
features_one = train[["Pclass", "Sex", "Age", "Fare"]].values

# モデル(決定木の作成)の作成
clf = DecisionTreeClassifier(random_state=0)
clf = clf.fit(features_one, target)

# 「test」の説明変数の値を取得
test_features = test[["Pclass", "Sex", "Age", "Fare"]].values

# 予測と結果の表示
pred = clf.predict(test_features)
print(pred)

f:id:taxa_program:20180519000042p:plain

こんな感じで1と0の羅列が結果として得られました。

Kaggleに投稿してみる

この予測データをCSVへ書き出してKaggleへ投稿してみましょう。
下記のコードでPassengerIdと予測値を取得してCSVファイルを書き出します。

# PassengerIdを取得
PassengerId = np.array(test["PassengerId"]).astype(int)

# my_prediction(予測データ)とPassengerIdをデータフレームへ落とし込む
my_solution = pd.DataFrame(my_prediction, PassengerId, columns = ["Survived"])

# my_tree_one.csvとして書き出し
my_solution.to_csv("my_tree_one.csv", index_label = ["PassengerId"])

結果は・・・

f:id:taxa_program:20180519001513p:plain

結果として「0.71291」のスコアが獲得できました。

Kaggleのスコアはコンペにより異なるようです。(各コンペの「Evaluation」のページに詳細が記載されています)

今回予測を行なったタイタニックのコンペでは予測スコアは単純に「Accuracy(正解率)」が使われていますので、今作った「my_tree_one」は約71.3%の確率で正解を予測できましたということになります。

いかがでしたでしょうか。

次回はもう少し精度をあげる努力をしてみます。