Kaggle入門①

f:id:kimoppy126:20180817081201p:plain

さぁやって参りました。

Kaggle入門編第一回目。 Kaggleを制するにはKernelを制すべし。って誰かが言っていたので、適当なKernelを動かしてみることにしました。
今回は、Samson Qian氏(きっとスゴイ人)のKernelである
Titanic: Beginner's Guide with sklearn

をはじめから丁寧に。やっていきましょう。

はじめに

scikit-learn (pythonの機械学習フレームワーク) を使用してタイタニック号に乗船していた乗客それぞれについて、 生存していたか否かを推測してやろうという取り組みです。

データの可視化、解析、機械学習の入門に最適な内容です。

データのダウンロード

mkdir titanic
cd titanic
wget https://www.kaggle.com/c/titanic/download/gender_submission.csv
wget https://www.kaggle.com/c/titanic/download/test.csv
wget https://www.kaggle.com/c/titanic/download/train.csv

titanicディレクトリを作成して、その中に必要なデータをダウンロードしましょう。

f:id:kimoppy126:20180817082001p:plain データはKernelのDataカラムから取得可能です。 今回はwgetコマンドでダウンロードしますが、KaggleのAPIを使ってダウンロードもできちゃうみたいです。

ライブラリ、パッケージの読み込み

import numpy as np 
import pandas as pd 

import seaborn as sns
from matplotlib import pyplot as plt
%matplotlib inline
sns.set_style("whitegrid")

import warnings
warnings.filterwarnings("ignore")

%matplotlib inlineは、ipython notebookで使用することが前提となってますね。
以下を参考に%matplotlib inlineは自動で設定されるようにしておきましょう。
参考:%matplotlib inlineを毎回書かなくて済む方法 - Qiita

warnings.filterwarnings("ignore") では、警告文を無視するような設定を行っています。

データの読み込み、閲覧

training = pd.read_csv("../input/train.csv")
testing = pd.read_csv("../input/test.csv")
training.head()

#   PassengerId  Survived  Pclass                                               Name     Sex   Age  SibSp  Parch            Ticket     Fare Cabin Embarked
#0            1         0       3                            Braund, Mr. Owen Harris    male  22.0      1      0         A/5 21171   7.2500   NaN        S
#1            2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1      0          PC 17599  71.2833   C85        C
#2            3         1       3                             Heikkinen, Miss. Laina  female  26.0      0      0  STON/O2. 3101282   7.9250   NaN        S
#3            4         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1      0            113803  53.1000  C123        S
#4            5         0       3                           Allen, Mr. William Henry    male  35.0      0      0            373450   8.0500   NaN        S

機械学習では、モデルの学習に使用するデータを学習データ (Train Data)、検証に使用するデータをテストデータ (Test Data)といいます。
基本的にこの2つのデータセットは用意されているものと思って問題ないです。

今回の場合、trainingは学習データ、testingはテストデータを意味しています。
head()モジュールで先頭5行のデータを出力しています。

training.describe()

#       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare
#count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000
#mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208
#std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429
#min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000
#25%     223.500000    0.000000    2.000000   20.125000    0.000000    0.000000    7.910400
#50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200
#75%     668.500000    1.000000    3.000000   38.000000    1.000000    0.000000   31.000000
#max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

describe()モジュールを使えば、データの詳細な統計値を知ることができます。 Rのsummary()関数と同じですね。

欠損値の扱い (Imputation)

def null_table(training, testing):
    print("Training Data Frame")
    print(pd.isnull(training).sum()) 
    print(" ")
    print("Testing Data Frame")
    print(pd.isnull(testing).sum())

null_table(training, testing)

#Training Data Frame
#PassengerId      0
#Survived         0
#Pclass           0
#Name             0
#Sex              0
#Age            177
#SibSp            0
#Parch            0
#Ticket           0
#Fare             0
#Cabin          687
#Embarked         2
#dtype: int64
#
#Testing Data Fra
#PassengerId      0
#Pclass           0
#Name             0
#Sex              0
#Age             86
#SibSp            0
#Parch            0
#Ticket           0
#Fare             1
#Cabin          327
#Embarked         0
#dtype: int64

null_table関数を定義します。 この関数内で、pd.isnull(training).sum()では、各カラムごとに欠損値の数をカウントしています。

出力結果から、Cabinカラムは欠損値が多いようです。
また、Ticket変数に関しては、生存者数の推定に使用する変数として不適切なので、データフレームから取り除きます。 変数の除去には、drop関数を用います。

training.drop(labels = ["Cabin", "Ticket"], axis = 1, inplace = True)
testing.drop(labels = ["Cabin", "Ticket"], axis = 1, inplace = True)

null_table(training, testing)

#Training Data Frame
#PassengerId      0
#Survived         0
#Pclass           0
#Name             0
#Sex              0
#Age            177
#SibSp            0
#Parch            0
#Fare             0
#Embarked         2
#dtype: int64
#
#Testing Data Frame
#PassengerId     0
#Pclass          0
#Name            0
#Sex             0
#Age            86
#SibSp           0
#Parch           0
#Fare            1
#Embarked        0
#dtype: int64

ここで一度、欠損値を抜いた場合のデータの分布を見ます。
copy変数に一時的なデータのコピーを用意し、dropna関数で欠損値の除去、seaborn.distplot関数でデータの分布を可視化します。

copy = training.copy()
copy.dropna(inplace = True)
sns.distplot(copy["Age"])

f:id:kimoppy126:20180817095423p:plain

中央値(median)で欠損値を埋めます。欠損値の置き換えには、fillnaモジュールを用います。

training["Age"].fillna(training["Age"].median(), inplace = True)
testing["Age"].fillna(testing["Age"].median(), inplace = True) 
training["Embarked"].fillna("S", inplace = True)
testing["Fare"].fillna(testing["Fare"].median(), inplace = True)

null_table(training, testing)

#Training Data Frame
#PassengerId    0
#Survived       0
#Pclass         0
#Name           0
#Sex            0
#Age            0
#SibSp          0
#Parch          0
#Fare           0
#Embarked       0
#dtype: int64
#
#Testing Data Frame
#PassengerId    0
#Pclass         0
#Name           0
#Sex            0
#Age            0
#SibSp          0
#Parch          0
#Fare           0
#Embarked       0
#dtype: int64

データのプロット、可視化

機械学習のモデルに用いるデータを理解し、可視化することは、 データの傾向を知ることができ、

性別 (Sex)

生存者の数を、男女別に可視化します。

sns.barplot(x="Sex", y="Survived", data=training)
plt.title("Distribution of Survival based on Gender")
plt.show()

f:id:kimoppy126:20180817100032p:plain

total_survived_females = training[training.Sex == "female"]["Survived"].sum()
total_survived_males = training[training.Sex == "male"]["Survived"].sum()

print("Total people survived is: " + str((total_survived_females + total_survived_males)))
print("Proportion of Females who survived:") 
print(total_survived_females/(total_survived_females + total_survived_males))
print("Proportion of Males who survived:")
print(total_survived_males/(total_survived_females + total_survived_males))

#Total people survived is: 342
#Proportion of Females who survived:
#0.6812865497076024
#Proportion of Males who survived:
#0.31871345029239767

棒グラフでは生存者だけでなく全体に対する割合を、標準出力では生存者中の割合を出力していることに注意してください。

結果から、性別の違いが生存率に大きな影響を与えていることが示唆されます。 これを見ると、性別は良い特微量であると判断できそうです。

同様にして、ほかの変数に関しても、特微量として適切かどうか、検討していきましょう。

階級 (PClass)

乗客の階級ごとの生存者数を棒グラフでプロットします。

sns.barplot(x="Pclass", y="Survived", data=training)
plt.ylabel("Survival Rate")
plt.title("Distribution of Survival Based on Class")
plt.show()

f:id:kimoppy126:20180817113024p:plain

total_survived_one = training[training.Pclass == 1]["Survived"].sum()
total_survived_two = training[training.Pclass == 2]["Survived"].sum()
total_survived_three = training[training.Pclass == 3]["Survived"].sum()
total_survived_class = total_survived_one + total_survived_two + total_survived_three

print("Total people survived is: " + str(total_survived_class))
print("Proportion of Class 1 Passengers who survived:") 
print(total_survived_one/total_survived_class)
print("Proportion of Class 2 Passengers who survived:")
print(total_survived_two/total_survived_class)
print("Proportion of Class 3 Passengers who survived:")
print(total_survived_three/total_survived_class)

#Total people survived is: 342
#Proportion of Class 1 Passengers who survived:
#0.39766081871345027
#Proportion of Class 2 Passengers who survived:
#0.2543859649122807
#Proportion of Class 3 Passengers who survived:
#0.347953216374269

先ほど同様に、棒グラフでは生存者だけでなく全体に対する割合を、標準出力では生存者中の割合を出力しています。

性別ごとに分けて生存者数の棒グラフをプロットします。

sns.barplot(x="Pclass", y="Survived", hue="Sex", data=training)
plt.ylabel("Survival Rate")
plt.title("Survival Rates Based on Gender and Class")

f:id:kimoppy126:20180817112047p:plain

sns.barplot(x="Sex", y="Survived", hue="Pclass", data=training)
plt.ylabel("Survival Rate")
plt.title("Survival Rates Based on Gender and Class")

f:id:kimoppy126:20180817112000p:plain

結果から、Pclass 1の人は、他の2つのPclassの人より生存率が高くなることが示唆されました。

年齢 (Age)

年齢が生存率に与えている影響があるか確かめるため、生存者、非生存者で分けてヒストグラムを描きます。

survived_ages = training[training.Survived == 1]["Age"]
not_survived_ages = training[training.Survived == 0]["Age"]
plt.subplot(1, 2, 1)
sns.distplot(survived_ages, kde=False)
plt.axis([0, 100, 0, 100])
plt.title("Survived")
plt.ylabel("Proportion")
plt.subplot(1, 2, 2)
sns.distplot(not_survived_ages, kde=False)
plt.axis([0, 100, 0, 100])
plt.title("Didn't Survive")
plt.show()

f:id:kimoppy126:20180817112713p:plain

分布をプロットするには、seaborn.stripplotを使います。 f:id:kimoppy126:20180817113440p:plain

結果から、若い年齢の人は、高齢の人より生存率が高くなる傾向があることが示唆されます。

変数同士の相関を見る

それぞれの変数同士の相関を見たい場合は、seaborn.pairplotを用います。

sns.pairplot(training)

f:id:kimoppy126:20180817123425p:plain

とりあえず

続きは

www.kimoton.com