Python
メモ
scikit-learn
pandas
自分用メモ

カテゴリ変数に負けずに python で機械学習する(ダミー変数化)

いつも同じことを調べているので、
これを機にメモにしておきました。

ここでは実際に 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 への
入力等で苦労した…悲しい…