scikit-learnライブラリを利用してロジスティック回帰を行なってみます。利用するデータはkaggleのTitanicデータです。
Titanicのデータは下記のページから取得できます(アカウント登録が必要です)。今回はtrain.csvとtest.csvを利用します。
Titanic: Machine Learning from Disaster
関連ページ
データフレームの基本操作[pandas][DataFrame][Titanic]
スポンサーリンク
開発環境
Python 3.5.1 :: Anaconda 2.5.0 (x86_64)
jupiter 4.0.6 scikit-learn 0.17
pandas 0.18.0 numpy 1.10.4
訓練用(train.csv)とテスト用(test.csv)のデータを読み込みます。
1 2 3 4 5 6 7 8 9 10 11 |
import pandas as pd import numpy as np from pandas import DataFrame from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score %matplotlib inline # ファイルを読み込む train_df = pd.read_csv('train.csv') test_df = pd.read_csv('test.csv') |
1 2 |
#訓練用データのデータ数、平均などの統計量を出力 train_df.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 |
Ageには欠損値があることが分かります。
1 2 |
#テスト用データのデータ数、平均などの統計量を出力 test_df.describe() |
結果
PassengerId | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|
count | 418.000000 | 418.000000 | 332.000000 | 418.000000 | 418.000000 | 417.000000 |
mean | 1100.500000 | 2.265550 | 30.272590 | 0.447368 | 0.392344 | 35.627188 |
std | 120.810458 | 0.841838 | 14.181209 | 0.896760 | 0.981429 | 55.907576 |
min | 892.000000 | 1.000000 | 0.170000 | 0.000000 | 0.000000 | 0.000000 |
25% | 996.250000 | 1.000000 | 21.000000 | 0.000000 | 0.000000 | 7.895800 |
50% | 1100.500000 | 3.000000 | 27.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 1204.750000 | 3.000000 | 39.000000 | 1.000000 | 0.000000 | 31.500000 |
max | 1309.000000 | 3.000000 | 76.000000 | 8.000000 | 9.000000 | 512.329200 |
訓練用データと同様にAgeには欠損値があることが分かります。またテスト用データには目的変数であるSurvivedカラム(生存の場合は1)が無いことが分かります。
これから訓練用データを利用してロジスティック回帰モデルを構築していきます。最終的にはそのモデルからテスト用データのSurvivedを予測します。
1 2 |
#訓練用データ train_df.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 |
1 2 |
#テスト用データ test_df.head() |
結果
PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 892 | 3 | Kelly, Mr. James | male | 34.5 | 0 | 0 | 330911 | 7.8292 | NaN | Q |
1 | 893 | 3 | Wilkes, Mrs. James (Ellen Needs) | female | 47.0 | 1 | 0 | 363272 | 7.0000 | NaN | S |
2 | 894 | 2 | Myles, Mr. Thomas Francis | male | 62.0 | 0 | 0 | 240276 | 9.6875 | NaN | Q |
3 | 895 | 3 | Wirz, Mr. Albert | male | 27.0 | 0 | 0 | 315154 | 8.6625 | NaN | S |
4 | 896 | 3 | Hirvonen, Mrs. Alexander (Helga E Lindqvist) | female | 22.0 | 1 | 1 | 3101298 | 12.2875 | NaN | S |
今回は説明変数(特徴量)として、乗船クラス階級(Pclass)と性別(Sex)及び年齢(Age)のみに着目します。それ以外の説明変数はあらかじめ削除しておきます。
1 2 3 |
#利用しない変数は削除します train_df = train_df.drop(['Name','SibSp','Parch','Ticket','Fare','Cabin','Embarked'],axis=1) test_df = test_df.drop(['Name','SibSp','Parch','Ticket','Fare','Cabin','Embarked'],axis=1) |
1 2 |
#訓練用 train_df.head() |
結果
PassengerId | Survived | Pclass | Sex | Age | |
---|---|---|---|---|---|
0 | 1 | 0 | 3 | male | 22.0 |
1 | 2 | 1 | 1 | female | 38.0 |
2 | 3 | 1 | 3 | female | 26.0 |
3 | 4 | 1 | 1 | female | 35.0 |
4 | 5 | 0 | 3 | male | 35.0 |
1 2 |
#テスト用 test_df.head() |
結果
PassengerId | Pclass | Sex | Age | |
---|---|---|---|---|
0 | 892 | 3 | male | 34.5 |
1 | 893 | 3 | female | 47.0 |
2 | 894 | 2 | male | 62.0 |
3 | 895 | 3 | male | 27.0 |
4 | 896 | 3 | female | 22.0 |
欠損値の補間
次にAgeの欠損値ですが、今回は男女それぞれの平均年齢で補間しておきます。
1 2 3 4 5 6 7 8 9 10 |
#年齢の欠損値を男女の平均年齢で補間 age_train_mean = train_df.groupby('Sex').Age.mean() def fage(x): if x.Sex == 'male': return round(age_train_mean['male']) if x.Sex == 'female': return round(age_train_mean['female']) train_df.Age.fillna(train_df[train_df.Age.isnull()].apply(fage,axis=1),inplace=True) |
テスト用(test.csv)のデータも、訓練用データと同じく欠損値を平均年齢で補間しておきます。
1 2 3 4 5 6 7 8 9 |
age_test_mean = test_df.groupby('Sex').Age.mean() def fage(x): if x.Sex == 'male': return round(age_test_mean['male']) if x.Sex == 'female': return round(age_test_mean['female']) test_df.Age.fillna(test_df[test_df.Age.isnull()].apply(fage,axis=1),inplace=True) |
ダミー変数
PclassとSexそれぞれの説明変数(特徴量)をモデルに組み込むための作業を行います。
まず男性と女性の生存確率を考えます。
1 2 3 |
#クロス集計 sex_ct = pd.crosstab(train_df['Sex'], train_df['Survived']) sex_ct |
結果
Survived | 0 | 1 |
---|---|---|
Sex | ||
female | 81 | 233 |
male | 468 | 109 |
結果を見て分かるように女性の方が明らかに生存確率が高い(Survivedが1は生存を表す)ことが分かります。そこで女性が1となるようなモデルにするため、train_df及びtest_dfデータフレームそれぞれにFemaleカラムを追加します。
1 2 3 4 5 |
#Femaleカラムを追加し、Sex要素のmale/femaleを1/0に変換して、要素として追加する train_df['Female'] = train_df['Sex'].map( {'male': 0, 'female': 1} ).astype(int) test_df['Female'] = test_df['Sex'].map( {'male': 0, 'female': 1} ).astype(int) train_df.head() |
結果
PassengerId | Survived | Pclass | Sex | Age | Female | |
---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | male | 22.0 | 0 |
1 | 2 | 1 | 1 | female | 38.0 | 1 |
2 | 3 | 1 | 3 | female | 26.0 | 1 |
3 | 4 | 1 | 1 | female | 35.0 | 1 |
4 | 5 | 0 | 3 | male | 35.0 | 0 |
Sexカラムの値がfemaleの場合、Femaleカラムの値が1となっていることを確認します。
次にPclass(乗船クラス階級)を見ていきます。
1 2 3 |
#クロス集計 pclass_ct = pd.crosstab(train_df['Pclass'], train_df['Survived']) pclass_ct |
結果
Survived | 0 | 1 |
---|---|---|
Pclass | ||
1 | 80 | 136 |
2 | 97 | 87 |
3 | 372 | 119 |
Pclassが3の場合、圧倒的に生存確率が低いのが分かります。そこでPclassをダミー変数に分け、3を除外して1と2を説明変数としてモデルに組み込んでいきます。これはダミー変数を全て含めると完全に相関する変数をモデルに含めてしまうことを避けるためでもあります(多重共線性)。
1 2 3 4 5 |
#Pclassをダミー変数で分ける pclass_train_df = pd.get_dummies(train_df['Pclass'],prefix='Class') pclass_test_df = pd.get_dummies(test_df['Pclass'],prefix='Class') pclass_train_df.head() |
結果
Class_1 | Class_2 | Class_3 | |
---|---|---|---|
0 | 0.0 | 0.0 | 1.0 |
1 | 1.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 1.0 |
3 | 1.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 1.0 |
1 2 3 4 5 6 7 |
#Class_3を削除 pclass_train_df = pclass_train_df.drop(['Class_3'], axis=1) pclass_test_df = pclass_test_df.drop(['Class_3'], axis=1) #Class_1,Class_2カラムを追加 train_df = train_df.join(pclass_train_df) test_df = test_df.join(pclass_test_df) |
1 2 |
#訓練用 train_df.head() |
結果
PassengerId | Survived | Pclass | Sex | Age | Female | Class_1 | Class_2 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | male | 22.0 | 0 | 0.0 | 0.0 |
1 | 2 | 1 | 1 | female | 38.0 | 1 | 1.0 | 0.0 |
2 | 3 | 1 | 3 | female | 26.0 | 1 | 0.0 | 0.0 |
3 | 4 | 1 | 1 | female | 35.0 | 1 | 1.0 | 0.0 |
4 | 5 | 0 | 3 | male | 35.0 | 0 | 0.0 | 0.0 |
1 2 |
#テスト用 test_df.head() |
結果
PassengerId | Pclass | Sex | Age | Female | Class_1 | Class_2 | |
---|---|---|---|---|---|---|---|
0 | 892 | 3 | male | 34.5 | 0 | 0.0 | 0.0 |
1 | 893 | 3 | female | 47.0 | 1 | 0.0 | 0.0 |
2 | 894 | 2 | male | 62.0 | 0 | 0.0 | 1.0 |
3 | 895 | 3 | male | 27.0 | 0 | 0.0 | 0.0 |
4 | 896 | 3 | female | 22.0 | 1 | 0.0 | 0.0 |
モデルの生成と予測
最終的に訓練データ(train_df)の余分なカラムを除外して、ロジスティック回帰モデルを構築し学習させます。一般的に行列は大文字で表します。
1 2 3 4 5 6 7 8 |
X = train_df.drop(['PassengerId','Survived','Pclass','Sex'],axis=1) y = train_df.Survived #モデルの生成 clf = LogisticRegression() #学習 clf.fit(X, y) |
1 2 |
#学習したモデルの精度 clf.score(X,y) |
結果
0.80134680134680136
精度とは、学習したモデルに伴う生存率の予測(predict_y)と実際の値(y)を比べた結果のことです。よって以下の計算式と同じになります。
1 2 3 4 5 |
#モデルに伴う生存率の予測値 predict_y = clf.predict(X) #実際の値と予測値の比率 accuracy_score(y, predict_y) |
結果
0.80134680134680136
学習したモデルの係数を表示させてみます。
1 2 3 |
#変数名とその係数を格納するデータフレーム coeff_df = DataFrame([X.columns, clf.coef_[0]]).T coeff_df |
結果
0 | 1 | |
---|---|---|
0 | Age | -0.0335092 |
1 | Female | 2.46126 |
2 | Class_1 | 2.16662 |
3 | Class_2 | 1.08844 |
Femaleのオッズ比を計算してみます。
1 |
print(np.exp(2.46126)) |
結果
11.7195688977
最後にテスト用データ(test_df)の生存者(Survived)を、生成したモデルから予測します。
1 2 3 |
#テストデータから生存者を予測 test_predict = clf.predict(test_df) print(test_predict) |
結果
[0 0 0 0 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1 1 0 0 1
1 0 0 0 0 0 1 1 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 0 0 0 1 1 1 1 0 0 1 1 0 1 0
1 0 0 1 0 1 0 0 0 0 0 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0
1 1 1 1 0 0 1 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0
0 0 1 0 0 1 0 0 1 1 0 1 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1
0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 0 1 0
1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 1 1 0 1 0 0 0 0 0 1
0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 1 0 0 0 0 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0
1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 1 0 1 1 0
0 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 0 0
0 1 1 1 1 1 0 1 0 0 0]
上記結果を改めてPassengerIdと共にテーブルで表示させてみます。
1 2 3 |
test_df = pd.read_csv('data/test.csv') result_df = pd.DataFrame({'PassengerId': test_df['PassengerId'], 'Survived':np.array(test_predict)}) result_df.head() |
結果
PassengerId | Survived | |
---|---|---|
0 | 892 | 0 |
1 | 893 | 0 |
2 | 894 | 0 |
3 | 895 | 0 |
4 | 896 | 1 |
参照ページ
sklearn.linear_model.LogisticRegression
PyData.Tokyo Tutorial & Hackathon #1