いつも同じことを調べているので、
これを機にメモにしておきました。
ここでは実際に python でcsv を読み込み、
ダミー変数化などの前処理を行ってSVMで予測を行いました。
モチベ
データを機械学習に突っ込む際に、データにカテゴリ変数(例:性別、出身国)が入っている場合があります。
そのときただの数値(例:日本なら1、アメリカなら2)に変えた場合、意図しない意味をデータに
与えてしまうので、学習がうまくいかないことがあります。
ここではダミー変数という方法でカテゴリ変数を数値に変換して対処します。
ダミー変数とは
例えば、以下のようなデータを想定します。
- ここでは国という一列だけ持っている
- 国は全部で
{日本, アメリカ, 中国}
しかない - 元のデータでは
国
という列があって、その中に{日本, アメリカ, 中国}
のいずれかが
格納されているとする
ダミー変数化では、国
という列を、国.日本, 国.アメリカ, 国.中国
という3つの列にして、
該当する値だけ 1
にして、その他を 0
とした形に変換します。
ダミー変数化前データ例
国 |
---|
日本 |
ダミー変数化後データ例
国.日本 | 国.アメリカ | 国.中国 |
---|---|---|
1 | 0 | 0 |
実装
データの取得
今回は「Adult Income Data Set」という匿名化加工などの実験で用いられるデータを用いました。
おそらくググっても手に入りますが、今回は R で取得しました(python と言っているのに早速 R を使っているのは気にしないでください)。
また、このデータセットには income(収入)
という項目があり、large, small, NaN
の3つの値があります。
今回の実装では NaN(欠損値)
に対し、large or small
を予測したいとします。
そのため NaN
が入っていない行を訓練データ、入っているデータを評価用データとしました。
library('arules')
data("AdultUCI")
id <- 1:nrow(AdultUCI)
d <- data.frame(id, AdultUCI)
write.csv(d, "AdultDataSet.csv", quote = FALSE, fileEncoding = 'cp932', row.names = FALSE)
ライブラリとcsvファイルの読み込み
import numpy as np
import pandas as pd
from sklearn import svm
df = pd.read_csv("AdultDataSet.csv", encoding='cp932', low_memory=False)
前処理(ダミー変数化も行う)
# 訓練ラベル
Y_train = df.copy()
Y_train['income'] = Y_train['income'].map({"large":1, "small":0})
Y_train = Y_train[Y_train['income'].notnull()]
Y_train = Y_train.iloc[:, 15].values # income のみ
# カテゴリ変数のダミー変数作成
X = df.iloc[:, 0:15] # income 以外
colnames_categorical = ['workclass', 'marital.status', 'occupation', 'relationship', 'race', 'sex', 'native.country']
X_dummy = pd.get_dummies(X[colnames_categorical], drop_first=True)
# ダミー変数の結合
X = pd.merge(X, X_dummy, left_index=True, right_index=True)
# 使わない、重複している列の削除
X = X.drop(colnames_categorical, axis=1)
X = X.drop(['id', 'education'], axis=1)
# income が NaN か否かで train と test を分ける
X_train = X[df['income'].notnull()].values
X_test = X[df['income'].isnull()].values
学習と予測
# 学習
clf = svm.LinearSVC() # 学習が速いから。他には svm.SVC(kernel='rbf') 等がある
print('start!')
clf.fit(X_train, Y_train)
print('end!')
# 予測
Y_predict = clf.predict(X_test)
予測した結果の結合
# 予測した値を加える
df2 = df.copy()
df2.loc[df2['income'].isnull(), 'income'] = Y_predict
df2['income'] = df2['income'].map({1.:"large", 0.:"small", "small":"small", "large":"large"})
df2.head()
結果の確認
本来は予め正解ラベルがついているデータを分類して性能を評価するべきだと思いますが、
今回は欠損しているデータに対して予測したことと、趣旨はダミー変数化の適用ですので
とりあえず欠損値がなくなっていることを確認してみます。
# 学習前の income の集計値
count_before = df['income'].value_counts(dropna=False)
pd.DataFrame(count_before) # print(count_before) でもよい
# 学習後の income の集計値
count_after = df2['income'].value_counts(dropna=False)
pd.DataFrame(count_after)
これで学習後に NaN
が消えていればひとまずOKです。
結果の出力
df2.to_csv('AfterAdultDataSet.csv', index=False)
さいごに
そこまで難しいことではないはずなのに、pandas の使い方とかscikit learn への
入力等で苦労した…悲しい…