未経験からデータサイエンティスト

未経験からデータサイエンティストの勉強したことの備忘録とか雑記とか

決定木でのモデル作成の仕組み

引き続き下記書籍でPythonでのデータ分析についてを勉強しているsue124です。
今回は第5章で使った決定木でのモデル作成についてです。
サンプルコードをそのまま写せばサンプルデータの決定木モデルを作ることができましたが、その仕組みの解説はなかったため自分で調べました。せっかくなので、それをブログに書いていきたいと思います。

まず決定木とはどんなものかというと、、

・教師ありの機械学習
・作成したモデルを下図のように表すことができ、視覚的に理解しやすい
・数値データ、カテゴリーデータが混在していてもOK
・オーバーフィッティングしてしまいやすい(枝分かれの深さを適切に設定する必要がある)

上記のような特徴がある決定木に関して、その仕組みを自分なりに調べた結果が下記です。
もし誤りがあれば、ご指摘いただけると幸いです。


今回は「どういう条件でピクニックに出かけるか?」という問題を例に考えます。
そのためには下図のような「どの程度2つの状態が混ざっているか?」を定量的に評価する必要があります。

f:id:sue124:20200312000654p:plain

扱っているデータが2つのクラスに分離できて、各クラスのデータ数がそれぞれm個、n個である場合、平均情報量(エントロピー)を以下のように表すことができます。

 I(\frac{m}{m+n}, \frac{n}{m+n}) = \frac{m}{m+n}\log_{2}\frac{m+n}{m} +  \frac{n}{m+n}\log_{2}\frac{m+n}{n}

m=1, n=0 のとき  I(0, 0) = 0
m=n のとき     I(\frac{1}{2}, \frac{1}{2}) = 1

エントロピーが下がるほど、分離が進んでいるということができます。
上式の対数の底は何でも良いのですが、クラス数に合わせると上記のように完全に分離できている時に0、全く分離できていない時に1となるので、わかりやすくなります。

なお、クラスが3つ以上(N個)のときは以下のようになります。

 I(P_{1}, P_{2}, ..., P_{N}) = \sum_{i=0}^N P_i \log_{N}\frac{1}{P_i} = -\sum_{i=0}^N P_i \log_{N}P_i
 P_{i} はクラスi のデータの割合。

これによって、最初に挙げた「どういう条件でピクニックに出かけるか?」に関して初期状態が以下のような状態であったとすると、

 全データ数:5個
 Yes:2個
 No:3個

この時のエントロピーは以下のようになります。
 I(\frac{2}{5}, \frac{3}{5}) = \frac{2}{5}\log_{2}\frac{5}{2} +  \frac{3}{5}\log_{2}\frac{5}{3} = 0.972

これを「天気(晴れ、曇り、雨)」で分類して下図のような状態になったとします。

f:id:sue124:20200312083755p:plain

この時のエントロピーは以下のようになります。
全体のエントロピー  = 1 × \frac{2}{5} + 1 × \frac{2}{5} + 0 × \frac{1}{5} = 0.8

天気で分けることによって、エントロピーが下がっているので、分離が進んでいると判断できます。
「晴れ」と「曇り」がまだ Yes と No が混ざっている状態なので、この後さらに別の指標(例えば気温、風速など)で分類していくことになります。


まとめ
決定木では平均情報量(エントロピーで分離度合いを評価し、平均情報量(エントロピー)が下がるように分離を進める。


###2020/06/07 追記###
書籍の中で使用している「sklearn.tree.DecisionTreeClassifier」のモジュールではCARTというアルゴリズムでジニ不純度を指標にして決定木を構築していることがわかったので、エントロピーとジニ不純度の違いをまとめてみました。
sue124.hatenablog.com

pythonでpandas.DataFrameを結合する

「毎月記事を書く」宣言してから、1記事も書かないまま2月になっちゃいました。
今月は2記事書かなきゃ。


しばらくは下記書籍の勉強をしていく中で、初めて見たものを備忘録的に書いていきたいと思います。

今回は分析対象のデータ(csv等)が複数のデータに散らばっている場合に、それをpythonのpandasに入れてから結合する関数、メソッドについてを書いていきます。 


「concat」でのcolumns方向に結合

下記のdf1, df2をpandas.concat()で以下のように結合することができます。

import pandas as pd

df1 = pd.DataFrame({"a": ["a1", "a2", "a3"],
                    "b": ["b1", "b2", "b3"],
                    "c": ["c1", "c2", "c3"],
                    "d": ["d1", "d2", "d3"]},
                    index=[0, 1, 2])

df2 = pd.DataFrame({"a": ["a4", "a5", "a6", "a7"],
                    "b": ["b4", "b5", "b6", "b7"],
                    "c": ["c4", "c5", "c6", "c7"],
                    "d": ["d4", "d5", "d6", "d7"]},
                    index=[3, 4, 5, 6])

df_result1 = pd.concat([df1, df2])

f:id:sue124:20200213061551p:plain

引数のリストの中身を増やせば、3つ以上DataFrameを結合することもできます。

df3 = pd.DataFrame({"a": ["a7", "a8"],
                    "b": ["b7", "b8"],
                    "c": ["c7", "c8"],
                    "d": ["d7", "d8"]},
                   index=[7, 8])

df_result2 = pd.concat([df1, df2, df3])
df_result2

f:id:sue124:20200219060823p:plain

columnが異なるDataframで実施すると以下のようになります。
元のDataFrameではデータが存在しなかった部分には"NaN" (Not a Number) が入ります。

df4 = pd.DataFrame({"a": ["a2", "a5", "a8"],
                    "b": ["b2", "b5", "b8"],
                    "z": ["z2", "z5", "z8"],},
                   index=[2, 5, 8])
df4

df_result3 = pd.concat([df1, df4])

f:id:sue124:20200219061617p:plain

上記では元のDataFrameのindexがそのまま使われていますが、 ignore_index=True を指定するとindexをゼロから連番で振り直すことができます。

df_result4 = pd.concat([df1, df4], ignore_index=True)

f:id:sue124:20200220062810p:plain

「concat」でのindex方向に結合
index方向に連結したい場合は、axis=1 を指定します。
なお上記でこれまで紹介したcolumn方向への結合を明示的に実施するには axis=0 (デフォルト) を指定します。

df_result5 = pd.concat([df1, df4], axis=1)

f:id:sue124:20200220062841p:plain

これまでは元のDataFrameの全てのデータが残っていましたが join="inner" を指定すると共通のラベルのみを残して連結することになります。
なお全てのデータを残して結合するのを明示的するには、join="outer" (デフォルト) を指定します。

df_result6 = pd.concat([df1, df4], axis=1, join="inner")

f:id:sue124:20200220062914p:plain

特定のラベルのみを指定する場合は、join_axes で指定します。

df_result7 = pd.concat([df1, df4], axis=1, join_axes=[df1.index])

f:id:sue124:20200220063017p:plain


index方向への結合時、indexが重複して見づらい場合がある。
このような時は keysで指定したラベルを追加することができる。

df_result8 = pd.concat([df1, df4], axis=1, keys=["X", "Y"])

f:id:sue124:20200220063357p:plain



「append」での結合

concatで大抵の結合はできるのですが、 DataFrame.append でよりシンプルに書けるようです。

df1.append(df2)

f:id:sue124:20200221232221p:plain

2つ以上のDataFrameをappendする場合は引数をリストで渡します。

df1.append([df2, df4])

f:id:sue124:20200221232323p:plain

1行だけを追加する場合はpandas.Seriesを引数に設定します。
nameをして指定ない場合は、ignore_index=True を指定するとエラーにならず、append元のindexの続きの番号が振られるようです。

s1 = pd.Series(["x0", "x1", "x2", "x3"],
               index=["a", "b", "c", "d"], name=10)
df1.append(s1)

f:id:sue124:20200222231411p:plain


まとめ
pandas.concat でcolumn方向、index方向にDataFrameを結合できる
DataFrame.append でも可(よりシンプルに書ける)

2020年の目標

2020年に入って半月以上過ぎてしまいましたが、まだ今年の目標をたててなかったので、ここに書いていきたいと思います。

 

・ブログを月1回以上更新する

 「教えることは、二度学ぶことである」という言葉を見習って、人に教えるつもりで勉強したことを書いていきます。

 

・「面白い」と思えることを追い求める

  ただプログラミング勉強するだけでなく、それを使って「面白い」と思えることをしていきたいと思います。人に喜んでもらえればなおよし。

 

・人との交流を積極的にする

 結構内向的な性格をしているので、これまでは割と1人で勉強してるだけでしたが、それでは頭でっかちになるだけで全く発展性がありませんでした。30歳になってやっと人からフィードバックをもらうことの大切さがわかった(遅い)ので、外に出て色んな人と話をして、物事を面白い方向に広げていければなぁと思います。

 

割と抽象的な感じになってしまいましたが、これを目標に頑張っていきたいと思います。

ブログ始めました

このブログでは何か面白いことをしたくてプログラミング勉強中の筆者が、勉強したことを書いていきたいと思います。

 

ひとまずは

 

Pythonでのデータ分析

・ラズパイでのカメラを動かす

 

とかをやっていく予定です。

具体的なことは追々書いていこうと思います。