Python

【毒キノコの分類】get_dummiesによるエンコーディングと決定木を用いた例

各種ライブラリのインポート

今回必要のないものも含んでいるので必要がなければ削除して使ってください。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import sklearn.preprocessing as sp
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
import pydotplus
from IPython.display import Image
from sklearn.model_selection import train_test_split 
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn import tree
# Scikit-learn(評価算出)
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn import metrics
# graphvizのdotファイルを生成する
from sklearn import datasets
from sklearn.cluster import MiniBatchKMeans
from sklearn import tree

ファイルの読み込み

train=pd.read_table('./train.tsv')
test=pd.read_table('./test.tsv')
sub=pd.read_csv('./sample_submit.csv')

欠損値の確認

欠損値の有無は必ず確認するようにしています。

学習データとテストデータ両方とも確認しましょう。

train.isnull().sum()
id                          0
Y                           0
cap-shape                   0
cap-surface                 0
cap-color                   0
bruises                     0
odor                        0
gill-attachment             0
gill-spacing                0
gill-size                   0
gill-color                  0
stalk-shape                 0
stalk-root                  0
stalk-surface-above-ring    0
stalk-surface-below-ring    0
stalk-color-above-ring      0
stalk-color-below-ring      0
veil-type                   0
veil-color                  0
ring-number                 0
ring-type                   0
spore-print-color           0
population                  0
habitat                     0
dtype: int64
 

テストデータも同様に実行(表示しませんが、欠損値はありませんでした)

pandasでカテゴリ変数をダミー変数に変換

文字列が入っている項目を符号化することは「エンコーディング」と呼び、機械学習では精度に関わる重要な作業となります。

機械学習では文字列を数値化しなければ計算することができません。

そこで例えば「赤、青、白」と言う文字列を「1,2,3」と単純に数値化してしまうと1~3は連続値と認識されてしまい、学習に悪影響が出てしまいます。

そこで、今回はget_dummiesを使います。

get_dummiesはカテゴリ変数の項目毎に、カテゴリ変数の値がその項目なら「1」、そうでないなら「0」になる新たな列を作成してくれます。

X_train=pd.get_dummies(train[['gill-color','gill-attachment','odor','cap-color']])
X_train['Y']=train['Y']
X_train.head()

結果、このようにgill-colorには「b,e,g・・・」などの文字として入力されていたものがそれぞれ項目として出力され「0」「1」で入力されます。

  • その他のエンコーディング手法

    ★列数を増やすエンコーディング
     ・OheHot Encoding
     ・Hash Encoding
    ★列数を増やさないエンコーディング
     ・Ordinal Encoding
     ・Target Encoding

ダミー変数としたデータの割合を知る

データセットの中身を知る上で説明変数内に何がどの割合存在するかを知っておくと何かと便利です。

まずは目的変数のYについてp(毒)の場合は「1」をそれ以外には「0」を入力します。

X_train['classes']=X_train['Y'].map(lambda x:1 if x=='p' else 0)
X_train['classes']
<bound method Series.sum of 0       1
1       1
2       0
3       1
4       1
       ..
4057    1
4058    1
4059    1
4060    1
4061    1
Name: classes, Length: 4062, dtype: int64>

 

それでは、説明変数とするcap-color_cやbにそれぞれいくつの対象が存在しているかを確認します。

この時、groupbyを使って要素をグループ化して処理すると見やすいです。

X_train.groupby(['cap-color_c','Y'])['Y'].count().unstack()

目的変数のYはどうでしょう。

ここでは試しにunstack()を抜いて出力してみます。

X_train.groupby('Y')['Y'].count()
Y
e    2103
p    1959
Name: Y, dtype: int64

無事にダミー変数を使ったエンコーディングができていることが確認できました。

ここまででデータの前処理は終わりです。

決定木を使ったコード例

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import sklearn.preprocessing as sp
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
import pydotplus
from IPython.display import Image
from sklearn.model_selection import train_test_split 
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn import tree
# Scikit-learn(評価算出)
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn import metrics
# graphvizのdotファイルを生成する
from sklearn import datasets
from sklearn.cluster import MiniBatchKMeans
from sklearn import tree

train=pd.read_table('./train.tsv')
test=pd.read_table('./test.tsv')
sub=pd.read_csv('./sample_submit.csv')

X_train=pd.get_dummies(train[['gill-color','gill-attachment','odor','cap-color']])
XX_test=pd.get_dummies(test[['gill-color','gill-attachment','odor','cap-color']])
X_train['Y']=train['Y']
X_train['classes']=X_train['Y'].map(lambda x:1 if x=='p' else 0)
X_train=X_train.drop('Y', axis=1)

Y=X_train['classes']
X=X_train.drop('classes', axis=1)

X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.05, random_state=1) 

clf=tree.DecisionTreeRegressor(max_depth=10, random_state=1)
# clf = RandomForestClassifier(n_estimators=200, max_depth=20, random_state=1) 
clf.fit(X_train, y_train)
y_pred = clf.predict(XX_test)
 
print ('Score: ',clf.score(X_test, y_test))#正解率の表示

#K分割交差検証
stratifiedkfold = StratifiedKFold(n_splits=10)  #K=10分割
scores = cross_val_score(clf, X_train, y_train, cv=stratifiedkfold)
print('Cross-Validation scores: {}'.format(scores))   # 各分割におけるスコア
print('Average score: {}'.format(np.mean(scores)))  # スコアの平均値


kmeans = MiniBatchKMeans(n_clusters=7, max_iter=300)
kmeans_result = kmeans.fit_predict(X_train)
features = X_train.columns

with open("tree.dot", 'w') as f:
    tree.export_graphviz(
        clf,
        out_file=f,
        feature_names=features,
        filled=True,
        rounded=True,  
        special_characters=True,
        impurity=False,
        proportion=False,
        class_names=list(map(str, range(0, np.max(kmeans_result)+1)))
    )
Score:  0.9851672614983253
Cross-Validation scores: [0.9700363  0.97976176 0.96777507 0.99717024 0.96404757 0.97596045
 0.98062093 0.96526717 0.97392876 0.98776665]
Average score: 0.9762334905066364

決定木の出力(max_depth=5の場合)

import pydotplus
from IPython.display import Image
 
graph = pydotplus.graphviz.graph_from_dot_file('tree.dot')
graph.write_png('tree.png')
Image(graph.create_png())

提出データへの変換

予測が0.5以上を1、それ以外を0としたのちに「p」と「e」に項目名を変換して提出とします。

提出時はラベルがない状態での提出なので削除して提出します。

for i in range(4062):
    if y_pred[i]>=0.5:
        y_pred[i]=1
    else:
        y_pred[i]=0   

sub['id'] = test['id']
sub['Y'] = list(map(int, y_pred))
sub['Y']=sub['Y'].replace([1, 0], ["p", "e"])
sub.to_csv('submission.csv', index=False)

以上です。

 

ABOUT ME
Mickey@コーヒー好きエンジニア
【製造業×プログラミング×AI】Python/VBAを活用した業務改善、Streamlit/Plotlyを活用したWebアプリ開発について初心者向けに発信中|趣味は自家焙煎コーヒー作り|noteでは焙煎理論を発信|ココナラではプログラミングに関する相談,就職/転職やコーヒーに関する相談などのサービスをやっています
【製造×プログラミング×AI】
Mickey@コーヒー好きエンジニア
【製造業×プログラミング×AI】ロボット×画像処理×AI×3現主義が得意な生産技術者|Python/VBAを活用した業務改善、Streamlit/Plotly/PySimpleGUIなどを活用したアプリ開発について初心者向けに発信中|趣味は自家焙煎コーヒー作り|noteでは焙煎理論を発信|ココナラではPython/iOS/VBA開発の支援,就職/転職相談などのサービスもやっています↓ Pythonを使ったWebアプリ開発を支援します 成果物が明確なのでPythonを学びたい人にオススメです
\ Follow me /