2018年7月28日土曜日

Pythonで分類木を視覚化する

都道府県別の飲み物の購入額をk-means法でクラスタリングするでk-means法によるクラスタリングで分類したが、どういう基準で分類されたのかわからない。そこでこのクラスタリングの結果をもとに決定木の一種である分類木を作成して、分類基準やその過程を確認してみる。scikit-learnで分類木による分類を行い、Graphvizで結果を視覚化する。


環境


Raspbian JessieとJupyter Notebook。



1.データの取得


使用するデータは都道府県別の飲み物の購入額をk-means法でクラスタリングするで使用したe-Statの2009年の全国消費実態調査データの都道府県ごとの「緑茶」「紅茶」「コーヒー」の消費額と、k-means法によるクラスタリングの結果。e-StatはAPIでデータ取得できるので、Pythonとe-StatのAPIで統計データを取得すると同じコード(estat.py)で2009年の全国消費実態調査データの都道府県ごとの「緑茶」「紅茶」「コーヒー」の消費額を取得し、k-means法によるクラスタリングを行う。

import pandas as pd
from sklearn.cluster import KMeans

import estat

def calc_rate(df):
    # データが文字列なので数値に変換
    df = df.apply(pd.to_numeric, errors='coerce')

    df['sum'] = df[df.columns[0]] + df[df.columns[1]] + df[df.columns[2]]
    df['緑茶R'] = df[df.columns[0]] / df['sum']
    df['紅茶R'] = df[df.columns[1]] / df['sum']
    df['コーヒーR'] = df[df.columns[2]] / df['sum']
 
    # 「緑茶」「紅茶」「コーヒー」の、都道府県ごとの購入金額の割合
    return df[df.columns[4:]]

if __name__ == '__main__':
    # e-Stat APIでデータ取得
    res = estat.get_estat_json()
    df = estat.to_dataframe(res)
 
    df = calc_rate(df)
    # 5つにクラスタリング
    df['cluster'] = KMeans(n_clusters=5, random_state=0).fit_predict(df.as_matrix())
    print(df)

結果は以下のようになる。

このデータを使って分類木を作成する。


2.Pythonで分類木を可視化するための準備


データの分類はscikit-learnでできるが、図の作成にはGraphvizが必要。pipでGraphvizバイナリとPythonパッケージがインストールできる。



3. 分類木による分類と可視化


分類木による分類はsklearn.tree.DecisionTreeClassifierクラスでできる。さらに分類結果をJupyter Notebookで可視化するコードは以下の通り。

from IPython.display import Image
from IPython.display import display

import pandas as pd
from sklearn.cluster import KMeans
from sklearn import tree
import graphviz 

import estat

def classification_tree(df):
    X = df[df.columns[:-1]]
    y = df[df.columns[-1]]

    # 最大深さ3の分類木を作成
    clf = tree.DecisionTreeClassifier(max_depth=3)    
    clf.fit(X, y)

    """
    分類木の可視化
    """
    # 分類木をDOT言語で出力
    dot_data = tree.export_graphviz(clf, out_file = None, feature_names = df.columns[:-1],
                           class_names = df.columns[-1], filled = True, rounded = True,
                           special_characters=True)
    graph = graphviz.Source(dot_data) 
    graph.format = 'png' # pngで保存
    display(Image(graph.render('ct_drink'))) # 保存したpngを表示
    
if __name__ == '__main__':
    # e-Stat APIでデータ取得
    res = estat.get_estat_json()
    df = estat.to_dataframe(res)
 
    df = calc_rate(df)
    # 5つにクラスタリング
    df['cluster'] = KMeans(n_clusters=5, random_state=0).fit_predict(df.as_matrix())
    print(df)

    classification_tree(df)


4.結果


分類木を可視化した結果は以下の通り。こうしてみると紅茶は緑茶、コーヒーに比べて割合が少なかったせいか、分類の判断に使われていない。




5.標準化してクラスタリングした結果で分類木を作成


クラスタリングする際にデータを標準化することで紅茶が分類基準に使われるようになるか試してみる。

以下のようにsklearn.preprocessing.StandardScalerクラスで標準化する。
    df = calc_rate(df)

上記コード部に以下のように標準化のコードを追加。
from sklearn.preprocessing import StandardScaler
...

    df = calc_rate(df)

    items = df.columns
    index = df.index
    df = pd.DataFrame(StandardScaler().fit_transform(df))
    df.columns = items #カラム名を再設定
    df.index = index # インデックス名を再設定

結果は以下の通り。クラスタリングの結果も変わっているが、今度はコーヒーが分類に使われていない。緑茶や紅茶の差の影響がより大きいということなのかもしれない。


標準化すると値がマイナスになって正しくクラスタリングされないと思われるので正規化に変更。5.を差し替え。(2018/7/31)

5.正規化してクラスタリングした結果で分類木を作成


クラスタリングする際にデータを正規化することで紅茶が分類基準に使われるようになるか試してみる。

以下のようにsklearn.preprocessing.MinMaxScalerクラスで正規化する。
    df = calc_rate(df)

上記コード部に以下のように正規化のコードを追加。
from sklearn.preprocessing import MinMaxScaler
...

    df = calc_rate(df)

    items = df.columns
    index = df.index
    df = pd.DataFrame(MinMaxScaler().fit_transform(df))
    df.columns = items #カラム名を再設定
    df.index = index # インデックス名を再設定

結果は以下の通り。クラスタリングの結果も変わっているが、今度は紅茶も分類に使われるようになった。


0 件のコメント:

コメントを投稿