Diary over Finite Fields

515ひかるの日記と雑文

get_dummies(pandas)について

pandasのダミー変数化の挙動

pandasというRのデータフレームやエクセルのようなデータ構造を扱うためのライブラリがある。そのpandasのダミー変数化の関数(pd.get_dummies)の挙動が、少しわかりにくい部分が*1あったのでここに書いておく。

前提

  • Python 3.6.0
  • numpy: version 1.11.3
  • pandas: version 0.19.2

型が数値(int, float)になっている場合

以下のような DataFrame をまず用意しておく。

>>> d = pd.DataFrame([[i + 5] for i in range(5)], columns=['col'])
>>> d
     col
0    5
1    6
2    7
3    8
4    9

ここでまず、 pd.get_dummiesdcol 列にのみ取り出して(pd.Series にした状態で)作用させてみる。

>>> type(d.col)
<class 'pandas.core.series.Series'>
>>> pd.get_dummies(d.col)
    5  6  7  8  9
0  1  0  0  0  0
1  0  1  0  0  0
2  0  0  1  0  0
3  0  0  0  1  0
4  0  0  0  0  1

このように正常にダミー変数化される。では、1列のDataFrameのままだとどうなるか。実際に実行すると、

>>> type(d)
<class 'pandas.core.frame.DataFrame'>
>>> pd.get_dummies(d)
   col
0    5
1    6
2    7
3    8
4    9

このようになる。特に変化はない。

なお、上の例では整数値で行ったが、 float 値のデータフレームを作っても同様の挙動を示す。

なぜこのような挙動を示すか

以下は推測。根拠はない。

これは正常の動作で、おそらく整数や float 型の列と、離散な値をとる列とが混在しているときにダミー変数化しすぎないようにするためだと思われる。

つまり、整数や少数などの数値データとして解釈できる列はダミー変数化はせず、数値データとして扱えない列のみダミー変数化する。要するに以下のような挙動を得るためである。

>>> import pandas as pd
>>> df = pd.DataFrame([['a', 1], ['b', 3], ['c', 11]], columns=['A', 'B'])
>>> df
   A   B
0  a   1
1  b   3
2  c  11
>>> pd.get_dummies(df)
    B  A_a  A_b  A_c
0   1    1    0    0
1   3    0    1    0
2  11    0    0    1

上の例では、 A 列は数値でなく、 B 列は数値なので B 列はそのまま、 A 列はダミー変数化される。

もし B 列もダミー変数化したいのであれば型を変換しておけばよい。

>>> df.dtypes
A    object
B     int64
dtype: object
>>> df.astype(object).dtypes
A    object
B    object
dtype: object
>>> pd.get_dummies(df.astype(object))
   A_a  A_b  A_c  B_1  B_3  B_11
0    1    0    0    1    0     0
1    0    1    0    0    1     0
2    0    0    1    0    0     1

一方で、 Series の場合は型は1種類なので、型が混在するようなことはなく、挙動が型によらずに一定なのだろう。

欠損が混じっている場合

欠損値(np.nan)が混じっている場合、 pd.get_dummies をした結果に欠損が残るか残らないかが上記の挙動により変わる*2。具体的には、以下のようになる。

>>> import numpy as np
>>> import pandas as pd
>>> d = pd.DataFrame([[1], [2], [np.nan]], columns=['col'])
>>> d
   col
0  1.0
1  2.0
2  NaN
>>> pd.get_dummies(d)
   col
0  1.0
1  2.0
2  NaN
>>> pd.get_dummies(d.col)
   1.0  2.0
0    1    0
1    0    1
2    0    0

欠損値が残っている場合でも、単に float 列のデータフレームには特に何も操作が行われないので、欠損値は残ったままだ。一方で、 Series の場合はダミー変数化され、すべての列に 0 が入る。これにより欠損を表す列ができていることにはなる*3

NaNを表す列を作りたければオプションがある:

>>>>>> pd.get_dummies(d.col, dummy_na=True)
    1.0   2.0  NaN
0     1     0     0
1     0     1     0
2     0     0     1

結論

  • ダミー変数化したい列は明示的に object 型に変換しておくほうが無難
  • NaNと戦うのしんどい

*1:個人の感想です。

*2:筆者はこれに数時間溶かしたのでこの記事を書いているのである。

*3:副作用のようなものだし、これをアテにするのはあまりよろしくはない気がする。