33
Machine Learning for Everyone Else Python 機械学習プログラミング データ分析演習編 ver2.0 2016/11/15 中井悦司 (Twitter @enakai00)

Python 機械学習プログラミング データ分析演習編

Embed Size (px)

Citation preview

Page 1: Python 機械学習プログラミング データ分析演習編

Machine Learning for Everyone Else

Python 機械学習プログラミングデータ分析演習編

ver2.0 2016/11/15中井悦司 (Twitter @enakai00)

Page 2: Python 機械学習プログラミング データ分析演習編

2

Python 機械学習プログラミング

目次■ 分析データの取り込みと確認

■ 分析データの可視化

■ scikit-learnによる機械学習処理

■ (オプション)カテゴリーデータの相関と仮説検定

■ (オプション)カイ二乗検定

Page 3: Python 機械学習プログラミング データ分析演習編

3

Python機械学習プログラミング

ハンズオン環境の利用方法

■ 本講義のハンズオン環境は、クラウド上の仮想マシンで用意してあります。下記のBlog記事の手順にしたがって、同じ環境を自分で用意することもできます。

- GCPでJupyterを利用する方法(GCEのVMインスタンスを利用する場合)

- http://enakai00.hatenablog.com/entry/2016/07/03/201117

■ Jupyter Notebookの利用方法については、下記の資料を参照してください。

- Python機械学習プログラミング データ分析ライブラリー解説編

- http://www.slideshare.net/enakai/it-pandas

Page 4: Python 機械学習プログラミング データ分析演習編

4

Python 機械学習プログラミング

分析データの取り込みと確認

Page 5: Python 機械学習プログラミング データ分析演習編

5

Python 機械学習プログラミング

データの取り込み■ Webで公開されているcsvデータをpandasのデータフレームに取り込みます。

- 取り込んだデータの説明は下記に記載されています。

● http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic3info.txt

- 数値自体に意味のないデータが数値で表現されている場合、誤った(意味のない)統計量を計算しないように、データ型を文字列型に変換しておきます。いまの場合、「pclass(社会的地位)」は数値で表現されていますが、この値の「平均値」を取っても特に意味はありません。

In [1]: import numpy as np import matplotlib.pyplot as plt import pandas as pd from pandas import Series, DataFrame

In [2]: data = pd.read_csv('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic3.csv') data['pclass'] = data['pclass'].map(str) # pclassの型を文字列型に変換

VARIABLE DESCRIPTIONS:pclass Passenger Class (1 = 1st; 2 = 2nd; 3 = 3rd)survived Survival (0 = No; 1 = Yes)name Namesex Sexage Agesibsp Number of Siblings/Spouses Aboardparch Number of Parents/Children Aboardticket Ticket Numberfare Passenger Farecabin Cabinembarked Port of Embarkation (C = Cherbourg; Q = Queenstown; S = Southampton)boat Lifeboatbody Body Identification Numberhome.dest Home/Destination

タイタニック号の乗船名簿の情報に、沈没による死亡情報を加えたものです。

Page 6: Python 機械学習プログラミング データ分析演習編

6

Python 機械学習プログラミング

データの全体感の把握■ 取り込んだデータの全体像を眺めて特徴を把握します。

- 今回使用するデータはそれほど大きくない(全部で1309件)ので、まずはスプレッドシートで開いて、どのようなデータか眺めておきます。

- 項目によっては欠損値(データが埋まっていないセル)が多いなどのデータセットとしての特徴、あるいは、「1歳未満の乳児が乗船している」などの社会的観点での特徴が見えてきます。

Page 7: Python 機械学習プログラミング データ分析演習編

7

Python 機械学習プログラミング

データのサマリー情報の確認■ 項目ごとのデータ数や平均値など、標準的な統計量を確認しておきます。

- 項目によってデータ数(count)が異なるのは、欠損値が存在するためです。

- 欠損値の影響で、パーセンタイルがうまく計算できいない部分があります。これらの項目は、次ページのように、欠損値を削除して計算します。

In [3]: data.columnsOut[3]: Index([u'pclass', u'survived', u'name', u'sex', u'age', u'sibsp', u'parch', u'ticket', u'fare', u'cabin', u'embarked', u'boat', u'body', u'home.dest'], dtype='object')

In [4]: data.describe()Out[4]:

Page 8: Python 機械学習プログラミング データ分析演習編

8

Python 機械学習プログラミング

データのサマリー情報の確認■ dropna()メソッドで欠損値を含む行を削除した上で、統計情報を確認します。

- 平均値(mean)と中央値(50%)が乖離している場合、値の分布に歪み(skew)があると考えられます。

In [5]: data[['age']].dropna().describe()In [6]: data[['fare']].dropna().describe()In [7]: data[['body']].dropna().describe()

平均値と中央値が乖離した例

Page 9: Python 機械学習プログラミング データ分析演習編

9

Python 機械学習プログラミング

分析データの可視化

Page 10: Python 機械学習プログラミング データ分析演習編

10

Python 機械学習プログラミング

■ 数値データはヒストグラムによって可視化します。

- 例として、年齢(age)と料金(fare)の分布をヒストグラムで確認します。

- 15歳未満の子供の分布、もしくは、200ポンド以上の高額料金の乗客などに分布の特徴が見られます。

In [8]: data[['age']].dropna().plot(kind='hist', bins=16)

In [9]: data[['fare']].dropna().plot(kind='hist', bins=20)グラフ化する際は、dropna()で

欠損値を削除しておきます。

数値データの可視化

Page 11: Python 機械学習プログラミング データ分析演習編

11

Python 機械学習プログラミング

数値データの相関の可視化■ 2つの数値データの関係性を見るときは、散布図で可視化します。

- 例として、年齢(age)と料金(fare)の関係を散布図で表示します。

In [10]: df = data[['age','fare']].dropna() df.plot(kind='scatter', x='age', y='fare')

- 特に目立った関係はありませんが、200ポンド以上の料金で乗船しているのは15歳以上に限られるなどが確認できます。

Page 12: Python 機械学習プログラミング データ分析演習編

12

Python 機械学習プログラミング

カテゴリーデータと数値データの相関の可視化■ カテゴリーデータと数値データの関係性を見るときは、箱ひげ図で可視化します。

- 例として、社会的地位(pclass)と料金(fare)の関係を箱ひげ図で表示します。

In [11]: df = data[['fare','pclass']].dropna() df.boxplot(column='fare', by='pclass')

- 社会的地位が高い人(pclass:1)は高額料金で乗船していることがわかります。

外れ値

75パーセンタイル

50パーセンタイル(中央値)

25パーセンタイル

Page 13: Python 機械学習プログラミング データ分析演習編

13

Python 機械学習プログラミング

3つ以上のデータの相関の可視化■ 3つ以上のデータの関係を表示する場合は、データの種類に応じて適切な可視化方法を選択する必要があります。

- たとえば、年齢(age)と料金(fare)の散布図を社会的地位(pclass)で色分けしてみます。

In [12]: df1 = data[data.pclass=='1'][['age','fare']].dropna() df2 = data[data.pclass=='2'][['age','fare']].dropna() df3 = data[data.pclass=='3'][['age','fare']].dropna()

plt.scatter(df1.age, df1.fare, facecolor='blue') plt.scatter(df2.age, df2.fare, facecolor='green') plt.scatter(df3.age, df3.fare, facecolor='red')

Page 14: Python 機械学習プログラミング データ分析演習編

14

Python 機械学習プログラミング

カテゴリーデータの相関■ カテゴリーデータの例として、性別(sex)と生存(survival)の相関を確認します。

- 2種類のカテゴリーデータの相関は、クロス集計表で確認します。

- この結果を見ると、性別によって生存率が大きく変わることが分かります。

- 同様の分析を社会的地位(pclass)と生存(survival)について行ってみてください。

- また、その他のデータについても自分なりの可視化を行って、特徴を発見してみてください。

In [13]: df = data[['sex','survived']].dropna() pd.crosstab(df.sex, df.survived)Out[13]:

In [14]: pd.crosstab(data.sex ,data.survived).plot(kind='bar')

In [15]: df.mean()Out[15]: survived 0.381971 # 平均生存率dtype: float64

In [16]: 339.0/(127+339)Out[16]: 0.7274678111587983 # 女性の生存率

Page 15: Python 機械学習プログラミング データ分析演習編

15

Python 機械学習プログラミング

scikit-learnによる機械学習処理

Page 16: Python 機械学習プログラミング データ分析演習編

16

Python 機械学習プログラミング

ロジスティック回帰■ 「性別」「年齢」の2つの特徴を用いたロジスティック回帰で、生存確率を予測するモ

デルを構築します。

- この後の分析で使用するモジュールをインポートします。

- 欠損値を含む行を削除して、分析に使用する列のみを含むDataFrameを用意します。ここでは、特徴変数 X とラベル y を個別にDataFrameとして作成しています。

- scikit-learnでは、分析に使用するデータを数値データに変換する必要があります。ここでは、性別を 0, 1 に変換した gender 列を用意します。

In [17]: from PIL import Image from sklearn.cross_validation import train_test_split, cross_val_score, KFold from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score from sklearn.tree import DecisionTreeClassifier, export_graphviz

In [18]: tmp = data[['age', 'sex', 'survived']].dropna() X = tmp[['age', 'sex']] y = tmp['survived'] X.head()

In [19]: X['gender'] = X['sex'].map({'female': 0, 'male': 1}).astype(int) X = X.drop(['sex'], axis=1) X.head()

In [18]: tmp = data[['age', 'sex', 'survived']].dropna() X = tmp[['age', 'sex']] y = tmp['survived'] X.head()

Page 17: Python 機械学習プログラミング データ分析演習編

17

Python 機械学習プログラミング

ロジスティック回帰- データセットをトレーニングセットとテストセットに分割した後、トレーニングセットを用いて

学習処理を実行します。

- 学習結果を用いて、トレーニングセットとテストセットに対する正解率を計算します。

- クロスバリデーションを実行する関数を用意します。

- クロスバリデーションを実施して、結果を表示します。

In [20]: X_train, X_val, y_train, y_val = train_test_split(X, y, train_size=0.8, random_state=1) clf = LogisticRegression() clf.fit(X_train, y_train)

In [22]: def cross_val(clf, X, y, K, random_state=0): cv = KFold(len(y), K, shuffle=True, random_state=random_state) scores = cross_val_score(clf, X, y, cv=cv) return scores

In [21]: y_train_pred = clf.predict(X_train) y_val_pred = clf.predict(X_val) print 'Accuracy on Training Set: %.3f' % accuracy_score(y_train, y_train_pred) print 'Accuracy on Validation Set: %.3f' % accuracy_score(y_val, y_val_pred)Out[21]:Accuracy on Training Set: 0.801Accuracy on Validation Set: 0.751

In [23]: clf = LogisticRegression() scores = cross_val(clf, X, y, 5) print 'Scores:', scores print 'Mean Score: %f ± %.3f' % (scores.mean(), scores.std())Out[23]:Scores: [ 0.81904762 0.80861244 0.75119617 0.77033493 0.74641148]Mean Score: 0.779121 ± 0.030

Page 18: Python 機械学習プログラミング データ分析演習編

18

Python 機械学習プログラミング

決定木(Decision Tree)■ 決定木を用いて、先ほどと同じ分析を実施します。

- クロスバリデーションを実施して、結果を表示します。

- 決定木を画像化して表示します。

● value は、survived = 0, 1 それぞれの

カテゴリーのデータ数を示します。

In [24]: clf = DecisionTreeClassifier(criterion='entropy', max_depth=2, min_samples_leaf=2) scores = cross_val(clf, X, y, 5) print 'Scores:', scores print 'Mean Score: %f ± %.3f' % (scores.mean(), scores.std())Out[24]:Scores: [ 0.8047619 0.75119617 0.78947368 0.77990431 0.74162679]Mean Score: 0.773393 ± 0.024

In [25]: clf.fit(X, y) export_graphviz(clf, out_file='tree.dot')In [26]: !dot -Tpng tree.dot -o tree.pngIn [27]: Image.open("tree.png")

女性 男性

Page 19: Python 機械学習プログラミング データ分析演習編

19

Python 機械学習プログラミング

(オプション)カテゴリーデータの相関と仮説検定

Page 20: Python 機械学習プログラミング データ分析演習編

20

Python 機械学習プログラミング

カテゴリーデータの相関■ カテゴリーデータの例として、性別(sex)と生存(survival)の相関を確認します。

- 2種類のカテゴリーデータの相関は、クロス集計表で確認します。

- 女性の生存率(0.73)は、平均生存率(0.38)よりも大幅に高いことがわかります。これほど大きな差が発生した理由としては、女性が優先的に救助されたなどの社会的背景が想像できますが・・・

- 仮にもう少し微妙な差であった場合、具体的にどれだけの差があれば、「統計学的に確かに差があった」と自信を持って主張できるのでしょうか?

In [13]: df = data[['sex','survived']].dropna() pd.crosstab(df.sex, df.survived)Out[13]:

In [14]: pd.crosstab(data.sex ,data.survived).plot(kind='bar')

In [15]: df.mean()Out[15]: survived 0.381971 # 平均生存率dtype: float64

In [16]: 339.0/(127+339)Out[16]: 0.7274678111587983 # 女性の生存率

Page 21: Python 機械学習プログラミング データ分析演習編

21

Python 機械学習プログラミング

仮説検定の考え方■ 「男女の生存率に差がある」と統計学的に自信を持って主張するには、次のような手続

きをとります。

- それが正しくないと仮定したモデル(帰無仮説)を用意します。

- そのモデルの下で「偶然」に同じ結果が得られる確率(p値)を計算します。

- 事前に決めておいた有意水準(例えば5%)よりp値が小さければ、「有意水準XX%で有意な差がある」と主張します。

■ このような手続きを「仮説検定」と呼びます。仮説検定においては、次の点に注意が必要です。

- 帰無仮説として利用するモデルによって、計算されるp値は異なります。「どのようなモデルの下に有意水準XX%なのか」という事まで説明しないと意味がありません。

- 仮説検定は、主張したい内容が正しいことを「証明する」手法ではありません。「統計的に偶然の差異とは考えづらいから、より詳細な調査を続ける価値がある」と判断するための手法です。

Page 22: Python 機械学習プログラミング データ分析演習編

22

Python 機械学習プログラミング

女性の生存率についての仮説検定■ 「男女の生存率に差がない」とする帰無仮説として、次のモデルを用いた仮説検定をお

こないます。

- 帰無仮説のモデル:男性843人+女性466人が乗船した船が沈没すると、男女関係なく、それぞれの人は、確率 0.38 で生存する。

- このモデルで船を沈没させた時に女性全体の生存率が R を越える確率をp値とする。

- p値が5%以下であれば、「生存率は男女によって異なる」と自信を持って主張できる。(「このモデルにおいては、有意水準5%で生存率は男女で異なる」と主張できる。)

■ ここでは、次の2つの問題を考えます。

問1. R = 0.72 の場合、有意水準5%で生存率は異なると主張できるか?

問2. 有意水準5%で生存率は異なると主張できなくなるのは、R がいくら以下の時か?

Page 23: Python 機械学習プログラミング データ分析演習編

23

Python 機械学習プログラミング

数値シュミレーションによる計算例■ このモデルにおけるp値は、理論的に計算することもできますが、ここでは、数値シュ

ミレーションによる計算を行います。

- モデルで設定した条件の船を10000回沈没させて、女性全体の生存率がどのように分布するかを観測します。

■ ただし、愚直に実装すると結構時間がかかります・・・。

In [17]: from numpy.random import rand def sink(n): result = [] df = data[['sex','survived']].dropna() for i in range(n): for index in range(0,len(df)): if rand() < 0.38: alive = True else: alive = False df.loc[index, 'survived'] = alive df_female = df[df.sex == 'female'] num_alive = len(df_female[df_female.survived == True]) result.append(float(num_alive) / len(df_female)) return result

In [18]: time result = sink(100)CPU times: user 35.8 s, sys: 34.8 ms, total: 35.9 sWall time: 35.8 s

100回沈没させるのに約36秒

Page 24: Python 機械学習プログラミング データ分析演習編

24

Python 機械学習プログラミング

データのシャッフルによるシュミレーション■ 元データをシャッフルする手法で、同じシュミレーションが効率化できます。

- この手法でシュミレーションを実施すると次の結果が得られました。

In [19]: def sink2(n): df = data[['survived','sex']].dropna() num_female = len(df[df.sex == 'female']) # num = 466 result = [] for i in range(n): df_shuffled = df.reindex(np.random.permutation(df.index)) samples = df_shuffled[:num_female] survive_rate = float(len(samples[samples.survived == 1]))/num_female result.append(survive_rate) return result

In [20]: result = sink2(10000) plt.hist(result)

- 女性の生存率が R=0.72 を越えることはありませんでした。(つまり p値は 0 で、問1の答えは Yes)

■ それでは、実際の女性の生存率が何割以下であれば、有意な差があるとは言えなくなるのでしょうか?

- ヒストグラムから目視確認するのは困難なので、違う種類のグラフを利用します。

Page 25: Python 機械学習プログラミング データ分析演習編

25

Python 機械学習プログラミング

CDF(累積分布関数)によるしきい値の確認■ シュミレーションで得られた10000個のサンプルを次の手順で整理します。

- 生存率 X = 0%, 1%, …, 100% の101個の値について、次を計算します。

● P(X) = 「生存率が X 以下のデータの個数の割合」

- P(X)が 95% に達する生存率 X を発見します。

⇒ これが、生存率が X 以上になるデータが全体の5%以下となるしきい値を与えます。    (これ以上 X が大きければ、「有意水準5%で有意な差がある」と言える。)

- この結果より、生存率 40% が有意水準5%のしきい値とわかります。

In [21]: count = np.array([0.0]*101) for i in result: i = int(i*100) for x in range(i ,101): count[x] += 1 cdf = count / len(result)

In [22]: plt.plot(range(30,46), cdf[30:46])

In [23]: cdf[40]Out[23]: 0.94230000000000003

この関数をCDF(累積分布関数)と呼びます。

Page 26: Python 機械学習プログラミング データ分析演習編

26

Python 機械学習プログラミング

(オプション)カイ二乗検定

Page 27: Python 機械学習プログラミング データ分析演習編

27

Python 機械学習プログラミング

3つ以上のカテゴリーのケース■ 先ほどと同じ方法で社会的地位(pclass)と生存(survival)の相関を確認します。

- 2種類のカテゴリーデータの相関は、クロス集計表で確認します。

In [24]: df = data[['pclass','survived']].dropna() pd.crosstab(df.pclass, df.survived)Out[24]:

In [25]: pd.crosstab(df.pclass, df.survived).plot(kind='bar')

- 社会的地位が高いほど生存率が高いことが読み取れます。この事実を検定するには、どのような確率を計算すればよいでしょうか?

Page 28: Python 機械学習プログラミング データ分析演習編

28

Python 機械学習プログラミング

3つ以上のカテゴリーのケース■ 仮に社会的地位と生存率に関連性がないとすれば、理想的には、それぞれのクラスの

「生存者数の期待値」は次のようになります。

■ この3つの値について、「(観測値 – 期待値)^2 / 期待値」の合計を計算します。

- この値を「カイ二乗統計量」と呼び、データシャッフルによるシュミレーションでこの値がどのように分布するかを確認します。(77以上の値になるサンプルがどの程度あるかで、上記の結果が偶然かどうかを判断します。)

In [26]: df.survived.mean()Out[26]: 0.3819709702062643

In [27]: df.survived.mean() * len(df[df.pclass == '1'])Out[27]: 123.37662337662337

In [28]: df.survived.mean() * len(df[df.pclass == '2'])Out[28]: 105.80595874713521

In [29]: df.survived.mean() * len(df[df.pclass == '3'])Out[29]: 270.81741787624139

In [30]: e1 = df.survived.mean() * len(df[df.pclass == '1']) e2 = df.survived.mean() * len(df[df.pclass == '2']) e3 = df.survived.mean() * len(df[df.pclass == '3']) ((200-e1)**2 / e1) + ((110-e2)**2 / e2) + ((181-e3)**2 / e3)Out[30]: 77.541616252803522

Page 29: Python 機械学習プログラミング データ分析演習編

29

Python 機械学習プログラミング

データシャッフルによるシュミレーション結果■ 実行結果は次のとおりです。

- 10000回のサンプリングで、カイ二乗統計量が77を越えることはありませんでしたので、有意水準5%で有意な差があると言えます。

※ ここではランダムサンプリングを用いて p 値を確認しましたが、カイ二乗統計量の確率分布は理論的に計算す ることが可能で、サンプリングを行わずに、理論計算だけで p 値を求めることも可能です。

In [31]: def sink3(n): df = data[['pclass','survived']].dropna() p1 = len(df[df.pclass == '1']) p2 = len(df[df.pclass == '2']) p3 = len(df[df.pclass == '3']) e1 = df.survived.mean() * p1 e2 = df.survived.mean() * p2 e3 = df.survived.mean() * p3

result = [] for i in range(n): df_suffled = df.reindex(np.random.permutation(df.index)) df1 = df_suffled[:p1] df2 = df_suffled[p1:p1+p2] df3 = df_suffled[p1+p2:] o1 = len(df1[df1.survived == 1]) o2 = len(df2[df2.survived == 1]) o3 = len(df3[df3.survived == 1]) result.append( ((o1-e1)**2/e1) + ((o2-e2)**2/e2) + ((o3-e3)**2/e3)) return result

In [32]: result = sink3(10000) max(result)Out[32]: 13.013916253312704

Page 30: Python 機械学習プログラミング データ分析演習編

30

Python 機械学習プログラミング

練習問題■ サイコロを60回振ったところ、それぞれの目が出た回数は次のようになりました。

- 1 : 9回、2 : 13回、3 : 7回、4 : 5回、5 : 17回、6 : 9回

■ ランダムサンプリングを用いたカイ二乗検定を用いて、このサイコロは偏りがないかどうかを議論してください。

- 偏りのないサイコロであれば、それぞれの回数の期待値は、10回ずつになります。

- したがって、この結果のカイ二乗統計量は次の計算から 9.4 になります。

In [33]: ((9-10)**2/10.0)+((13-10)**2/10.0)+((7-10)**2/10.0)+((5-10)**2/10.0)+((17-10)**2/10.0)+((9-10)**2/10.0)Out[33]: 9.4

Page 31: Python 機械学習プログラミング データ分析演習編

31

Python 機械学習プログラミング

解答用のコード実行例In [34]: dices = np.random.randint(1,7,(10000,60)) result = [] for sample in dices: chi2 = 0.0 for i in range(1,7): chi2 += (len([ 1 for c in sample if c == i ]) - 10.0)**2 / 10.0 result.append(chi2)

In [35]: max(result)Out[35]: 29.799999999999997

In [36]: plt.hist(result,bins=40)

In [37]: count = np.array([0.0]*36) for i in result: for x in range(int(i), 36): count[x] += 1 cdf = count / len(result) plt.plot(cdf[:21])

In [38]: cdf[9:12]Out[38]: array([ 0.9233, 0.9474, 0.965 ])

カイ2乗統計量が9.4を超える場合が存在します。

CDFのグラフを見ると、χ =9.4は、95%領域の境界あたりになります。

厳密に値を見ると、χ =10.0 は95%領域に含まれるので、サイコロに偏りがあるとは結論できません。

Page 32: Python 機械学習プログラミング データ分析演習編

32

Python 機械学習プログラミング

メモとしてお使いください

Page 33: Python 機械学習プログラミング データ分析演習編

Machine Learning for Everyone Else

Thank You!