調べたことやコード変更した内容について下記のようにコメントを入れることにしています。

In [1]:
  #【コメント】

Googleドライブのマウント

In [ ]:
from google.colab import drive
drive.mount('/content/drive')

0. データ表示

In [3]:
#from モジュール名 import クラス名(もしくは関数名や変数名)
import pandas as pd
from pandas import DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

#matplotlibをinlineで表示するためのおまじない (plt.show()しなくていい)
%matplotlib inline

以下では,Googleドライブのマイドライブ直下にstudy_ai_mlフォルダを置くことを仮定しています.必要に応じて,パスを変更してください。

In [59]:
# titanic data csvファイルの読み込み
titanic_df = pd.read_csv('/content/drive/My Drive/study_ai_ml_google/data/titanic_train.csv')
  #【コメント】エラー出るのでパス変更
In [60]:
# ファイルの先頭部を表示し、データセットを確認する
titanic_df.head(5)
Out[60]:
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
In [61]:
titanic_df
Out[61]:
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
... ... ... ... ... ... ... ... ... ... ... ... ...
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.0000 NaN S
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.0000 B42 S
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.4500 NaN S
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.0000 C148 C
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.7500 NaN Q

891 rows × 12 columns

In [ ]:
  #【コメント】PassengerId 乗客者ID
  #【コメント】Survived 生死
  #【コメント】Pclass 旅客クラス。裕福さの目安
  #【コメント】Name 	名前
  #【コメント】Sex 性別
  #【コメント】Age 	年齢←なんで浮動小数?(表記上そうみえるだけ?)
  #【コメント】SibSp 同乗している兄弟。配偶者の数
  #【コメント】Parch 同乗している親。子供の数
  #【コメント】Ticket チケット番号
  #【コメント】Fare 運賃
  #【コメント】Cabin 客室番号
  #【コメント】Embarked 出港地
  

1. ロジスティック回帰

不要なデータの削除・欠損値の補完

In [62]:
#予測に不要と考えるからうをドロップ (本当はここの情報もしっかり使うべきだと思っています)
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

#一部カラムをドロップしたデータを表示
titanic_df.head()
Out[62]:
Survived Pclass Sex Age SibSp Parch Fare Embarked
0 0 3 male 22.0 1 0 7.2500 S
1 1 1 female 38.0 1 0 71.2833 C
2 1 3 female 26.0 0 0 7.9250 S
3 1 1 female 35.0 1 0 53.1000 S
4 0 3 male 35.0 0 0 8.0500 S
In [7]:
#nullを含んでいる行を表示
titanic_df[titanic_df.isnull().any(1)].head(10)
Out[7]:
Survived Pclass Sex Age SibSp Parch Fare Embarked
5 0 3 male NaN 0 0 8.4583 Q
17 1 2 male NaN 0 0 13.0000 S
19 1 3 female NaN 0 0 7.2250 C
26 0 3 male NaN 0 0 7.2250 C
28 1 3 female NaN 0 0 7.8792 Q
29 0 3 male NaN 0 0 7.8958 S
31 1 1 female NaN 1 0 146.5208 C
32 1 3 female NaN 0 0 7.7500 Q
36 1 3 male NaN 0 0 7.2292 C
42 0 3 male NaN 0 0 7.8958 C
In [63]:
#Ageカラムのnullを中央値で補完

titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())
  #【コメント】中央値で保管することで影響を与えにくいデータにしつつ学習できるようなデータにしている?なんで中央値?平均はダメ?
  #【コメント】欠損値に対応する方法としては①そのデータを無視する②補完するの大きく2つあるらしい
  #【コメント】②ついてよくつかわれるのは中央値、平均値、最頻値らしい

#再度nullを含んでいる行を表示 (Ageのnullは補完されている)
titanic_df[titanic_df.isnull().any(1)]

#titanic_df.dtypes
Out[63]:
Survived Pclass Sex Age SibSp Parch Fare Embarked AgeFill
5 0 3 male NaN 0 0 8.4583 Q 29.699118
17 1 2 male NaN 0 0 13.0000 S 29.699118
19 1 3 female NaN 0 0 7.2250 C 29.699118
26 0 3 male NaN 0 0 7.2250 C 29.699118
28 1 3 female NaN 0 0 7.8792 Q 29.699118
... ... ... ... ... ... ... ... ... ...
859 0 3 male NaN 0 0 7.2292 C 29.699118
863 0 3 female NaN 8 2 69.5500 S 29.699118
868 0 3 male NaN 0 0 9.5000 S 29.699118
878 0 3 male NaN 0 0 7.8958 S 29.699118
888 0 3 female NaN 1 2 23.4500 S 29.699118

179 rows × 9 columns

1. ロジスティック回帰

実装(チケット価格から生死を判別)

In [66]:
#運賃だけのリストを作成
data1 = titanic_df.loc[:, ["Fare"]].values
In [67]:
#生死フラグのみのリストを作成
label1 =  titanic_df.loc[:,["Survived"]].values
In [68]:
from sklearn.linear_model import LogisticRegression
In [69]:
model=LogisticRegression()
In [70]:
model.fit(data1, label1)
/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py:985: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
Out[70]:
LogisticRegression()
In [84]:
model.predict([[62]])
Out[84]:
array([1])
In [15]:
model.predict_proba([[62]])
Out[15]:
array([[0.49978123, 0.50021877]])
In [86]:
  #【コメント】可視化してみる
x = np.arange(0,100,1)
x = x.reshape(-1,1)

y = model.predict(x)
plt.plot(x,y)
  #【コメント】61,62が生死の境界と学習されている模様
Out[86]:
[<matplotlib.lines.Line2D at 0x7f6c586a3bd0>]
In [94]:
X_test_value = model.decision_function(x) 
  #【コメント】各予測データの確信度を表す。
  #【コメント】正負がそれぞれのクラスに対応する
  #【コメント】ちょうど61,62あたりで正負が切り替わりそう
plt.plot(x,X_test_value)
Out[94]:
[<matplotlib.lines.Line2D at 0x7f6c5861d150>]
In [92]:
# 決定関数値(絶対値が大きいほど識別境界から離れている)
# X_test_value = model.decision_function(X_test) 
# # 決定関数値をシグモイド関数で確率に変換
# X_test_prob = normal_sigmoid(X_test_value) 
In [18]:
print (model.intercept_)

print (model.coef_)
[-0.94131796]
[[0.01519666]]
In [97]:
w_0 = model.intercept_[0]
w_1 = model.coef_[0,0]

# def normal_sigmoid(x):
#     return 1 / (1+np.exp(-x))

def sigmoid(x):
    return 1 / (1+np.exp(-(w_1*x+w_0)))

x_range = np.linspace(-1, 500, 3000)

plt.figure(figsize=(9,5))
#plt.xkcd()
plt.legend(loc=2)


# plt.ylim(-0.1, 1.1)
# plt.xlim(-10, 10)

# plt.plot([-10,10],[0,0], "k", lw=1)
# plt.plot([0,0],[-1,1.5], "k", lw=1)
plt.plot(data1,np.zeros(len(data1)), 'o')
plt.plot(data1, model.predict_proba(data1), 'o')
plt.plot(x_range, sigmoid(x_range), '-')
#plt.plot(x_range, normal_sigmoid(x_range), '-')
#

  #【コメント】青データのx軸射影(運賃の偏りの可視化?)
  #【コメント】黄,緑生死の確率
  #【コメント】黄,緑生死の確率
  #【コメント】赤学習モデルのシグモイド関数
No handles with labels found to put in legend.
Out[97]:
[<matplotlib.lines.Line2D at 0x7f6c58480890>]

1. ロジスティック回帰

実装(2変数から生死を判別)

In [98]:
#AgeFillの欠損値を埋めたので
#titanic_df = titanic_df.drop(['Age'], axis=1)
In [99]:
titanic_df['Gender'] = titanic_df['Sex'].map({'female': 0, 'male': 1}).astype(int)
In [100]:
titanic_df.head(3)
Out[100]:
Survived Pclass Sex Age SibSp Parch Fare Embarked AgeFill Gender
0 0 3 male 22.0 1 0 7.2500 S 22.0 1
1 1 1 female 38.0 1 0 71.2833 C 38.0 0
2 1 3 female 26.0 0 0 7.9250 S 26.0 0
In [104]:
titanic_df['Pclass_Gender'] = titanic_df['Pclass'] + titanic_df['Gender']
  #【コメント】旅客クラス+性別
  #【コメント】男性は旅客クラスを1下げるイメージ?
In [24]:
titanic_df.head()
Out[24]:
Survived Pclass Sex Age SibSp Parch Fare Embarked AgeFill Gender Pclass_Gender
0 0 3 male 22.0 1 0 7.2500 S 22.0 1 4
1 1 1 female 38.0 1 0 71.2833 C 38.0 0 1
2 1 3 female 26.0 0 0 7.9250 S 26.0 0 3
3 1 1 female 35.0 1 0 53.1000 S 35.0 0 1
4 0 3 male 35.0 0 0 8.0500 S 35.0 1 4
In [105]:
titanic_df = titanic_df.drop(['Pclass', 'Sex', 'Gender','Age'], axis=1)
In [106]:
titanic_df.head()
Out[106]:
Survived SibSp Parch Fare Embarked AgeFill Pclass_Gender
0 0 1 0 7.2500 S 22.0 4
1 1 1 0 71.2833 C 38.0 1
2 1 0 0 7.9250 S 26.0 3
3 1 1 0 53.1000 S 35.0 1
4 0 0 0 8.0500 S 35.0 4
In [27]:
# 重要だよ!!!
# 境界線の式
#   w_1・x + w_2・y + w_0 = 0
#   ⇒ y = (-w_1・x - w_0) / w_2
 
# # 境界線 プロット
# plt.plot([-2,2], map(lambda x: (-w_1 * x - w_0)/w_2, [-2,2]))
 
# # データを重ねる
# plt.scatter(X_train_std[y_train==0, 0], X_train_std[y_train==0, 1], c='red', marker='x', label='train 0')
# plt.scatter(X_train_std[y_train==1, 0], X_train_std[y_train==1, 1], c='blue', marker='x', label='train 1')
# plt.scatter(X_test_std[y_test==0, 0], X_test_std[y_test==0, 1], c='red', marker='o', s=60, label='test 0')
# plt.scatter(X_test_std[y_test==1, 0], X_test_std[y_test==1, 1], c='blue', marker='o', s=60, label='test 1')
In [28]:
np.random.seed = 0

xmin, xmax = -5, 85
ymin, ymax = 0.5, 4.5

index_survived = titanic_df[titanic_df["Survived"]==0].index
index_notsurvived = titanic_df[titanic_df["Survived"]==1].index

from matplotlib.colors import ListedColormap
fig, ax = plt.subplots()
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
  #【コメント】死亡者を年齢,旅客クラスで可視化(重なるとわかりにくいので乱数で散らせてる?)
sc = ax.scatter(titanic_df.loc[index_survived, 'AgeFill'],
                titanic_df.loc[index_survived, 'Pclass_Gender']+(np.random.rand(len(index_survived))-0.5)*0.1,
                color='r', label='Not Survived', alpha=0.3)
  #【コメント】生存者を年齢,旅客クラスで可視化(重なるとわかりにくいので乱数で散らせてる?)
sc = ax.scatter(titanic_df.loc[index_notsurvived, 'AgeFill'],
                titanic_df.loc[index_notsurvived, 'Pclass_Gender']+(np.random.rand(len(index_notsurvived))-0.5)*0.1,
                color='b', label='Survived', alpha=0.3)
ax.set_xlabel('AgeFill')
ax.set_ylabel('Pclass_Gender')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
ax.legend(bbox_to_anchor=(1.4, 1.03))
  #【コメント】見た感じPclass_Gender良いほど生存率高そうな傾向
  #【コメント】見た感じ年齢低いほど生存率高そうな傾向
Out[28]:
<matplotlib.legend.Legend at 0x7f6c6360ca10>
In [108]:
#運賃だけのリストを作成
data2 = titanic_df.loc[:, ["AgeFill", "Pclass_Gender"]].values
In [109]:
data2
Out[109]:
array([[22.        ,  4.        ],
       [38.        ,  1.        ],
       [26.        ,  3.        ],
       ...,
       [29.69911765,  3.        ],
       [26.        ,  2.        ],
       [32.        ,  4.        ]])
In [110]:
#生死フラグのみのリストを作成
label2 =  titanic_df.loc[:,["Survived"]].values
In [111]:
model2 = LogisticRegression()
In [112]:
model2.fit(data2, label2)
/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py:985: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
Out[112]:
LogisticRegression()
In [113]:
model2.predict([[10,1]])
Out[113]:
array([1])
In [35]:
model2.predict_proba([[10,1]])
  #【コメント】若くて旅客クラス良い人はかなり生存率高し
Out[35]:
array([[0.03754749, 0.96245251]])
In [114]:
titanic_df.head(3)
Out[114]:
Survived SibSp Parch Fare Embarked AgeFill Pclass_Gender
0 0 1 0 7.2500 S 22.0 4
1 1 1 0 71.2833 C 38.0 1
2 1 0 0 7.9250 S 26.0 3
In [37]:
h = 0.02
xmin, xmax = -5, 85
ymin, ymax = 0.5, 4.5
xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))
Z = model2.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)

fig, ax = plt.subplots()
levels = np.linspace(0, 1.0)
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
#contour = ax.contourf(xx, yy, Z, cmap=cm, levels=levels, alpha=0.5)

sc = ax.scatter(titanic_df.loc[index_survived, 'AgeFill'],
                titanic_df.loc[index_survived, 'Pclass_Gender']+(np.random.rand(len(index_survived))-0.5)*0.1,
                color='r', label='Not Survived', alpha=0.3)
sc = ax.scatter(titanic_df.loc[index_notsurvived, 'AgeFill'],
                titanic_df.loc[index_notsurvived, 'Pclass_Gender']+(np.random.rand(len(index_notsurvived))-0.5)*0.1,
                color='b', label='Survived', alpha=0.3)

ax.set_xlabel('AgeFill')
ax.set_ylabel('Pclass_Gender')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
#fig.colorbar(contour)

x1 = xmin
x2 = xmax
y1 = -1*(model2.intercept_[0]+model2.coef_[0][0]*xmin)/model2.coef_[0][1]
y2 = -1*(model2.intercept_[0]+model2.coef_[0][0]*xmax)/model2.coef_[0][1]
ax.plot([x1, x2] ,[y1, y2], 'k--')
  #【コメント】ロジスティック回帰で分類すると点線で分類される?
Out[37]:
[<matplotlib.lines.Line2D at 0x7f6c59a45d50>]

2. モデル評価

混同行列とクロスバリデーション

In [38]:
from sklearn.model_selection import train_test_split
In [117]:
traindata1, testdata1, trainlabel1, testlabel1 = train_test_split(data1, label1, test_size=0.2)
print(traindata1.shape)
print(trainlabel1.shape)
print(testdata1.shape)
print(testlabel1.shape)
  #【コメント】8:2でデータ分割
  #【コメント】シャッフルするかは引数で指定できる。デフォルトではシャッフルされる
(712, 1)
(712, 1)
(179, 1)
(179, 1)
In [40]:
traindata2, testdata2, trainlabel2, testlabel2 = train_test_split(data2, label2, test_size=0.2)
traindata2.shape
trainlabel2.shape
#本来は同じデータセットを分割しなければいけない。(簡易的に別々に分割している。)
Out[40]:
(712, 1)
In [41]:
data = titanic_df.loc[:, ].values
label =  titanic_df.loc[:,["Survived"]].values
traindata, testdata, trainlabel, testlabel = train_test_split(data, label, test_size=0.2)
traindata.shape
trainlabel.shape
Out[41]:
(712, 1)
In [42]:
eval_model1=LogisticRegression()
eval_model2=LogisticRegression()
#eval_model=LogisticRegression()
In [120]:
predictor_eval1=eval_model1.fit(traindata1, trainlabel1).predict(testdata1)
predictor_eval2=eval_model2.fit(traindata2, trainlabel2).predict(testdata2)
#predictor_eval=eval_model.fit(traindata, trainlabel).predict(testdata)
/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py:985: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py:985: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
In [44]:
eval_model1.score(traindata1, trainlabel1)
Out[44]:
0.6573033707865169
In [45]:
eval_model1.score(testdata1,testlabel1)
Out[45]:
0.6983240223463687
In [46]:
eval_model2.score(traindata2, trainlabel2)
Out[46]:
0.7556179775280899
In [47]:
eval_model2.score(testdata2,testlabel2)
  #【コメント】モデル2の方が誤差小さい
Out[47]:
0.8379888268156425
In [48]:
from sklearn import metrics
print(metrics.classification_report(testlabel1, predictor_eval1))
print(metrics.classification_report(testlabel2, predictor_eval2))
  #【コメント】precision適合率
  #【コメント】recall再現率
  #【コメント】f1-scoreF値
  #【コメント】support正解データに含まれている個数
              precision    recall  f1-score   support

           0       0.69      0.96      0.80       112
           1       0.78      0.27      0.40        67

    accuracy                           0.70       179
   macro avg       0.73      0.61      0.60       179
weighted avg       0.72      0.70      0.65       179

              precision    recall  f1-score   support

           0       0.86      0.91      0.89       123
           1       0.78      0.68      0.72        56

    accuracy                           0.84       179
   macro avg       0.82      0.79      0.80       179
weighted avg       0.83      0.84      0.83       179

In [49]:
from sklearn.metrics import confusion_matrix
confusion_matrix1=confusion_matrix(testlabel1, predictor_eval1)
confusion_matrix2=confusion_matrix(testlabel2, predictor_eval2)
In [50]:
confusion_matrix1
  #【コメント】混同行列
  #【コメント】model2に比べると偽陰性多し
Out[50]:
array([[107,   5],
       [ 49,  18]])
In [51]:
confusion_matrix2
Out[51]:
array([[112,  11],
       [ 18,  38]])
In [52]:
fig = plt.figure(figsize = (7,7))
#plt.title(title)
sns.heatmap(
    confusion_matrix1,
    vmin=None,
    vmax=None,
    cmap="Blues",
    center=None,
    robust=False,
    annot=True, fmt='.2g',
    annot_kws=None,
    linewidths=0,
    linecolor='white',
    cbar=True,
    cbar_kws=None,
    cbar_ax=None,
    square=True, ax=None, 
    #xticklabels=columns,
    #yticklabels=columns,
    mask=None)
  #【コメント】混同行列の可視化方法かなるほど
Out[52]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f6c59a45ad0>
In [53]:
fig = plt.figure(figsize = (7,7))
#plt.title(title)
sns.heatmap(
    confusion_matrix2,
    vmin=None,
    vmax=None,
    cmap="Blues",
    center=None,
    robust=False,
    annot=True, fmt='.2g',
    annot_kws=None,
    linewidths=0,
    linecolor='white',
    cbar=True,
    cbar_kws=None,
    cbar_ax=None,
    square=True, ax=None, 
    #xticklabels=columns,
    #yticklabels=columns,
    mask=None)
Out[53]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f6c62367c10>
In [54]:
#Paired categorical plots

import seaborn as sns
sns.set(style="whitegrid")

# Load the example Titanic dataset
titanic = sns.load_dataset("titanic")

# Set up a grid to plot survival probability against several variables
g = sns.PairGrid(titanic, y_vars="survived",
                 x_vars=["class", "sex", "who", "alone"],
                 size=5, aspect=.5)

# Draw a seaborn pointplot onto each Axes
g.map(sns.pointplot, color=sns.xkcd_rgb["plum"])
g.set(ylim=(0, 1))
sns.despine(fig=g.fig, left=True)

plt.show()
  #【コメント】シンプルにそれぞれ一要素だけで見ても生存率に傾向ありそう
/usr/local/lib/python3.7/dist-packages/seaborn/axisgrid.py:1209: UserWarning: The `size` parameter has been renamed to `height`; please update your code.
  warnings.warn(UserWarning(msg))
In [55]:
#Faceted logistic regression

import seaborn as sns
sns.set(style="darkgrid")

# Load the example titanic dataset
df = sns.load_dataset("titanic")

# Make a custom palette with gendered colors
pal = dict(male="#6495ED", female="#F08080")

# Show the survival proability as a function of age and sex
g = sns.lmplot(x="age", y="survived", col="sex", hue="sex", data=df,
               palette=pal, y_jitter=.02, logistic=True)
g.set(xlim=(0, 80), ylim=(-.05, 1.05))
plt.show()
  #【コメント】seabornつかうと簡単にロジスティック回帰の2値分類が可視化できますよってことかな?
/usr/local/lib/python3.7/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
In [55]: