一端の何かになれるか

一生懸命は眩しい

scikit-learnで決定木分析(CART)を試す

とにかく試して見るシリーズ第一弾。

なぜやるのか

 いつまでもデータマート拵えおじさんのままではマズいため、比較的難易度が低くビジネスで幅広く応用が効くらしい決定木分析をやってみる

決定木分析とは

概要

決定木分析 (Decision Tree Analysis) は、機械学習の手法の一つ。木を逆にしたようなデータ構造を用いて分類と回帰を行う。

決定木分析の特徴

  • 樹木状の構造で学習結果を視覚化でき、ルールをシンプルに表現できるため、論理的な解釈が容易
  • データの標準化 (正規化) やダミー変数の作成を必要としないため、前処理の手間がほとんど不要
  • カテゴリカルデータと数値データの両方を扱うことが可能
  • 検定を行って、作成したモデルの正しさを評価することが可能

参考にしたサイト*1

ビジネスでの活用例

  • 顧客別の購買履歴から自社の製品を購入している顧客の特徴を分析
  • 金融機関の取引履歴から顧客属性別の貸し倒れリスクを分析
  • 機械の動作ログから故障につながる指標を分析

参考にしたサイト*2

取り組んだ課題

Rの標準データセット[HairEyeColor]のデータを使ってHair[髪の色]、Eye Color[瞳の色]から、Sex[性別]を予測する決定木のモデルをPythonで作成する*3

試行過程と結果

1.データセットを確認する

Datasetはここから入手した

# 編集前のcsvファイルを読み込んで、DataFrame化
import pandas as pd
import_df = pd.read_csv('HairEyeColor.csv')
import_df.head()
Unnamed: 0 Hair Eye Sex Freq
0 1 Black Brown Male 32
1 2 Brown Brown Male 53
2 3 Red Brown Male 10
3 4 Blond Brown Male 3
4 5 Black Blue Male 11

決定木分析をするにあたり、このデータセットでは2工程の下処理が必要

  • 各レコードをFreqの数だけ生成する
  • String型の各項目をint型に置換する

2.CSVデータを加工する

参考にしたサイト*4

in_file  = open("HairEyeColor.csv","r")
out_file = open("HairEyeColor_Edited.csv","w")

# アウトプットファイルにヘッダーを書き込み
out_file.write("Hair,Eye,Sex\n")

# インプットファイルのヘッダーを読み飛ばす
in_file.readline()
# インプットファイルの全レコードを読み込み
lines = in_file.readlines()

# for文で1行ずつ処理
for line in lines:
    # 改行コードはブランクに置換
    line = line.replace("\n","")
    # カンマ区切りでリストに変換する
    line = line.split(",")
    # 変換処理した値を、更にカンマ区切りへ変換
    row = "{},{},{}\n".format(line[1],line[2],line[3])
    # 書き出し用のファイルに"Freq"の数だけ出力
    freq = int(line[4])
    for i in range(0,freq):
        out_file.write(row)

in_file.close()
out_file.close()
# 編集後のcsvファイルを読み込んで、DataFrame化
import_df = pd.read_csv('HairEyeColor_Edited.csv')
import_df.head()
Hair Eye Sex
0 Black Brown Male
1 Black Brown Male
2 Black Brown Male
3 Black Brown Male
4 Black Brown Male
# 加工前との整合性を確認する
check = import_df.groupby(['Hair','Eye','Sex']).size()
check
Hair   Eye    Sex   
Black  Blue   Female     9
              Male      11
       Brown  Female    36
              Male      32
       Green  Female     2
              Male       3
       Hazel  Female     5
              Male      10
Blond  Blue   Female    64
              Male      30
       Brown  Female     4
              Male       3
       Green  Female     8
              Male       8
       Hazel  Female     5
              Male       5
Brown  Blue   Female    34
              Male      50
       Brown  Female    66
              Male      53
       Green  Female    14
              Male      15
       Hazel  Female    29
              Male      25
Red    Blue   Female     7
              Male      10
       Brown  Female    16
              Male      10
       Green  Female     7
              Male       7
       Hazel  Female     7
              Male       7
dtype: int64
# データの特徴を見てみる
import_df.describe()
Hair Eye Sex
count 592 592 592
unique 4 4 2
top Brown Brown Female
freq 286 220 313
# String型の各項目をint型に置換するための辞書を作って変換
dict = {'Hair':{'Black' :1,'Blond':2,'Brown':3,'Red'  :4},
        'Eye' :{'Blue'  :1,'Brown':2,'Green':3,'Hazel':4},
        'Sex' :{'Female':1,'Male' :2}}

import_df['Hair'] = import_df["Hair"].map(dict['Hair'])
import_df['Eye']  = import_df["Eye"].map(dict['Eye'])
import_df['Sex']  = import_df["Sex"].map(dict['Sex'])
import_df.head()
Hair Eye Sex
0 1 2 2
1 1 2 2
2 1 2 2
3 1 2 2
4 1 2 2

3.決定木の分類器を作成して可視化する

参考にしたサイト*5

from sklearn import tree
# 説明変数は'Hair','Eye'
variables = ['Hair','Eye']

# 決定木の分類器を作成
classifier = tree.DecisionTreeClassifier()

# 目的変数は'Sex'
# サンプルデータで学習
classifier = classifier.fit(import_df[variables],import_df['Sex'])
# 作成した決定木を可視化する
import pydotplus
from sklearn.externals.six import StringIO
dot_data = StringIO()
tree.export_graphviz(classifier, out_file=dot_data,
                     filled=True,rounded=True)

graph = pydotplus.graph_from_dot_data(dot_data.getvalue())

from IPython.display import Image
Image(graph.create_png())

f:id:select_from_where:20170910163541p:plain
Decision Tree

4.交差検証

今回は決定木の深さだけを変えて交差検証してみる

# 交差検証1(max_depth指定なし)
import numpy as np
from sklearn import cross_validation as cv

data = import_df.reindex(np.random.permutation(import_df.index))
variables = ['Hair','Eye']

classifier = tree.DecisionTreeClassifier()
scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5)

print(scores.mean(), scores)
0.517007950507 [ 0.48739496  0.57142857  0.47058824  0.49152542  0.56410256]
# 交差検証2(max_depth=3)
data = import_df.reindex(np.random.permutation(import_df.index))
variables = ['Hair','Eye']

classifier = tree.DecisionTreeClassifier(max_depth=3)
scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5)

print(scores.mean(), scores)
0.545807753784 [ 0.46218487  0.52941176  0.56302521  0.59322034  0.58119658]
# 交差検証3(max_depth=4)
data = import_df.reindex(np.random.permutation(import_df.index))
variables = ['Hair','Eye']

classifier = tree.DecisionTreeClassifier(max_depth=4)
scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5)

print(scores.mean(), scores)
0.526948093449 [ 0.58823529  0.47058824  0.55462185  0.50847458  0.51282051]

感想

  • インプットしたHair, EyeのColorに対して、Sexをアウトプットする仕組みを作れないと意味がない
  • パラメータ設定によるチューニングができるほどの理解には及んでいないため、もう少し理解を深めたい
  • Grid Searchなるものを実行できるようになれば、パラメータを最適化できるらしい(理解度浅くても使えるということか)
  • レコメンドに応用するとして、説明変数と目的変数の粒度をどうするのかが重要そうだ(ファッションのように品目がかなり細かい商品を扱うならカテゴリで、ラインナップがファッションなどに比べて限定的な金融商品などなら商品そのものでも分類できそう)
  • 顧客の属性情報を説明変数に様々な切り口で分析をかければ、マーケティング対象をより詳細に理解して施策を練るのに役立てられそう

参考にしたサイト

*1:こちらのサイトを参考にしました

scikit-learn で決定木分析 (CART 法) – Python でデータサイエンス

*2:こちらのサイトを参考にしましたanalytics-news.jp

*3:こちらの問題を参考にしていますcodeiq.jp

*4:こちらのサイトを参考にしましたmemopy.hatenadiary.jp

*5:こちらのサイトを参考にしました
todoa2c.github.io