pandasでカテゴリ変数をダミー変数に変換(get_dummies)
pandasでカテゴリ変数(カテゴリカルデータ、質的データ)をダミー変数に変換するには、pandas.get_dummies()関数を使う。
文字列でカテゴリー分けされた性別などのデータを、男を0, 女を1のように変換したり、多クラスの特徴量をone-hot表現に変換したりすることができる。機械学習の前処理として行うことが多い。
ここでは以下の内容について説明する。
pandas.get_dummies()の基本的な使い方- 最初のカテゴリーを除外: 引数
drop_first - 欠損値
NaNもダミー化: 引数dummy_na pandas.DataFrameのダミー変数の列名を指定: 引数prefix,prefix_seppandas.DataFrameの列を指定して数値・ブール列もダミー化: 引数columns- 各カテゴリー(水準)を任意の数値化:
map()メソッド
例として以下のデータを使用する。説明のために列を追加している。
import pandas as pd
import numpy as np
df = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)
df['sex'] = ['female', np.nan, 'male', 'male', 'female', 'male']
df['rank'] = [2, 1, 1, 0, 2, 0]
print(df)
# age state point sex rank
# name
# Alice 24 NY 64 female 2
# Bob 42 CA 92 NaN 1
# Charlie 18 CA 70 male 1
# Dave 68 TX 70 male 0
# Ellen 24 CA 88 female 2
# Frank 30 NY 57 male 0
サンプルのcsvファイルはコチラ。
pandas.get_dummies()の基本的な使い方
第一引数dataにpanas.Series、配列(Pythonのリスト、NumPy配列ndarrayなど)、および、pandas.DataFrameを指定する。
いずれの場合もpandas.DataFrameの新たなオブジェクトが返される。元のオブジェクトを更新したい場合は、例えば以下のように元のオブジェクトに代入すればよい。
df = df.get_dummies(df)
引数にpandas.Series、配列を指定する場合
pandas.Series、配列(Pythonのリスト、NumPy配列ndarrayなど)の場合、カテゴリー名が列名になる。
print(pd.get_dummies(df['sex']))
# female male
# name
# Alice 1 0
# Bob 0 0
# Charlie 0 1
# Dave 0 1
# Ellen 1 0
# Frank 0 1
print(pd.get_dummies(['male', 1, 1, 2]))
# 1 2 male
# 0 0 0 1
# 1 1 0 0
# 2 1 0 0
# 3 0 1 0
print(pd.get_dummies(np.arange(6)))
# 0 1 2 3 4 5
# 0 1 0 0 0 0 0
# 1 0 1 0 0 0 0
# 2 0 0 1 0 0 0
# 3 0 0 0 1 0 0
# 4 0 0 0 0 1 0
# 5 0 0 0 0 0 1
配列(Pythonのリスト、NumPy配列ndarrayなど)は一次元配列である必要がある。二次元配列はエラーとなる。
# print(pd.get_dummies(np.arange(6).reshape((2, 3))))
# Exception: Data must be 1-dimensional
引数にpandas.DataFrameを指定する場合
pandas.DataFrameの場合は、デフォルトではデータ型dtypeがobject(おもに文字列)またはcategoryである列がすべてダミー変数化される。
数値(int, float)やブールboolの列は変換されず元のまま。数値やブールの列もダミー化したい場合の設定については後述。
pandas.DataFrameの場合の列名は元の列名_カテゴリー名となる。変更する設定は後述。
print(pd.get_dummies(df))
# age point rank state_CA state_NY state_TX sex_female sex_male
# name
# Alice 24 64 2 0 1 0 1 0
# Bob 42 92 1 1 0 0 0 0
# Charlie 18 70 1 1 0 0 0 1
# Dave 68 70 0 0 0 1 0 1
# Ellen 24 88 2 1 0 0 1 0
# Frank 30 57 0 0 1 0 0 1
最初のカテゴリーを除外: 引数drop_first
k個のカテゴリーをダミー化する場合、k-1個のダミー変数があればよいが、get_dummies()関数ではデフォルトでk個のダミー変数に変換される。
引数drop_first=Trueとすると、最初のカテゴリーが除外されk-1個のダミー変数に変換される。
print(pd.get_dummies(df, drop_first=True))
# age point rank state_NY state_TX sex_male
# name
# Alice 24 64 2 1 0 0
# Bob 42 92 1 0 0 0
# Charlie 18 70 1 0 0 1
# Dave 68 70 0 0 1 1
# Ellen 24 88 2 0 0 0
# Frank 30 57 0 1 0 1
欠損値NaNもダミー化: 引数dummy_na
デフォルトでは欠損値NaNは除外して処理される。
NaNも一つのカテゴリーとしてダミー変数化したい場合は、引数dummy_na=Trueとする。
このとき、NaNを含まない列に対してもNaNのダミー変数が生成される。すべての要素が0となる。
print(pd.get_dummies(df, drop_first=True, dummy_na=True))
# age point rank state_NY state_TX state_nan sex_male sex_nan
# name
# Alice 24 64 2 1 0 0 0 0
# Bob 42 92 1 0 0 0 0 1
# Charlie 18 70 1 0 0 0 1 0
# Dave 68 70 0 0 1 0 1 0
# Ellen 24 88 2 0 0 0 0 0
# Frank 30 57 0 1 0 0 1 0
DataFrameのダミー変数の列名を指定: 引数prefix, prefix_sep
pandas.DataFrameの場合、デフォルトでは生成されるダミー変数の列名は元の列名_カテゴリー名となる。
これを引数prefix, prefix_sepで変更できる。<prefix><prefix-sep>カテゴリー名となる。
引数prefixは、文字列、文字列のリスト、文字列の辞書のいずれかで指定する。
文字列の場合はすべてのプレフィックスが共通になる。ダミー変数の列名をカテゴリー名だけにしたい場合はprefix, prefix_sepを空の文字列''に指定すればOK。
print(pd.get_dummies(df, drop_first=True, prefix='', prefix_sep=''))
# age point rank NY TX male
# name
# Alice 24 64 2 1 0 0
# Bob 42 92 1 0 0 0
# Charlie 18 70 1 0 0 1
# Dave 68 70 0 0 1 1
# Ellen 24 88 2 0 0 0
# Frank 30 57 0 1 0 1
リスト、辞書の場合は、元の列名のままにしたい列に対しても値を指定する必要がある。リスト、辞書の要素数とダミー化する列の数が一致していないとエラーになる。
print(pd.get_dummies(df, drop_first=True, prefix=['ST', 'sex'], prefix_sep='-'))
# age point rank ST-NY ST-TX sex-male
# name
# Alice 24 64 2 1 0 0
# Bob 42 92 1 0 0 0
# Charlie 18 70 1 0 0 1
# Dave 68 70 0 0 1 1
# Ellen 24 88 2 0 0 0
# Frank 30 57 0 1 0 1
print(pd.get_dummies(df, drop_first=True, prefix={'state': 'ST', 'sex': 'sex'}, prefix_sep='-'))
# age point rank ST-NY ST-TX sex-male
# name
# Alice 24 64 2 1 0 0
# Bob 42 92 1 0 0 0
# Charlie 18 70 1 0 0 1
# Dave 68 70 0 0 1 1
# Ellen 24 88 2 0 0 0
# Frank 30 57 0 1 0 1
DataFrameの列を指定して数値・ブール列もダミー化: 引数columns
上述の通り、pandas.DataFrameの場合、デフォルトではデータ型dtypeがobject(おもに文字列)またはcategoryの列のみがダミー化される。
引数columnsにダミー化したい列の列名をリストで指定すると、数値やブールの列もダミー化できる。指定しない列はダミー化されない。
print(pd.get_dummies(df, drop_first=True, columns=['sex', 'rank']))
# age state point sex_male rank_1 rank_2
# name
# Alice 24 NY 64 0 0 1
# Bob 42 CA 92 0 1 0
# Charlie 18 CA 70 1 1 0
# Dave 68 TX 70 1 0 0
# Ellen 24 CA 88 0 0 1
# Frank 30 NY 57 1 0 0
大量の列をリストで指定するのが面倒な場合は、ダミー化したい数値やブールの列のデータ型をastype()でobjectに変換してしまうほうが楽かもしれない。
なお、列のデータ型をobjectに変換し元のオブジェクトを更新した場合は、その後その列を使って数値演算やブール演算を行う際に元の型に戻す必要があるので注意。
df['rank'] = df['rank'].astype(object)
print(pd.get_dummies(df, drop_first=True))
# age point state_NY state_TX sex_male rank_1 rank_2
# name
# Alice 24 64 1 0 0 0 1
# Bob 42 92 0 0 0 1 0
# Charlie 18 70 0 0 1 1 0
# Dave 68 70 0 1 1 0 0
# Ellen 24 88 0 0 0 0 1
# Frank 30 57 1 0 1 0 0
各カテゴリー(水準)を任意の数値化: map()メソッド
ダミー変数のように各カテゴリー(水準)に対して0 or 1の列を生成するのではなく、文字列で分類された各カテゴリーを任意の数値に置換したいときは、map()メソッドを使う。
引数に{元の値: 変換後の値}という辞書を指定する。
print(df['state'].map({'CA': 0, 'NY': 1, 'TX': 2}))
# name
# Alice 1
# Bob 0
# Charlie 0
# Dave 2
# Ellen 0
# Frank 1
# Name: state, dtype: int64
map()はpandas.Seriesのメソッド。pandas.DataFrameの列に対して処理して値を更新する場合は、以下のように元の列に代入すればOK。
df['state'] = df['state'].map({'CA': 0, 'NY': 1, 'TX': 2})
print(df)
# age state point sex rank
# name
# Alice 24 1 64 female 2
# Bob 42 0 92 NaN 1
# Charlie 18 0 70 male 1
# Dave 68 2 70 male 0
# Ellen 24 0 88 female 2
# Frank 30 1 57 male 0
map()による置換についての詳細は以下の記事を参照。