pandasのget_dummies()は使えない。そんな風に考えていた時期が俺にｍ

# pandasのget_dummies()は使えない

特徴量中のカテゴリ変数をダミー変数化する際、`pandas` の `get_dummies()` はとってもお手軽で便利なんだけど、

```python
import pandas as pd
df1 = pd.DataFrame([[1,'yukino',3,'f',4],[2,'teio',4,'m',3],[3,'nature',4]], columns=['id','name','age','sex','d'])
df1
```

| |id	|name	|age	|sex	|d|
|--|--|--|--|--|--|
|0	|1	|yukino	|3	|f	|4.0
|1	|2	|teio	|4	|m	|3.0
|2	|3	|nature	|4	|None	|NaN

```python
pd.get_dummies(df1) # すべてのobject型をdummy化
```

| |id	|age	|d	|name_nature	|name_teio	|name_yukino	|sex_f	|sex_m
|--|--|--|--|--|--|--|--|--|
|0	|1	|3	|4.0	|0	|0	|1	|1	|0
|1	|2	|4	|3.0	|0	|1	|0	|0	|1
|2	|3	|4	|NaN	|1	|0	|0	|0	|0

```python
pd.get_dummies(df1, columns=['sex']) # dummy化するカラムを指定
```

| |id	|name	|age	|d	|sex_f	|sex_m
|--|--|--|--|--|--|--|
|0	|1	|yukino	|3	|4.0	|1	|0
|1	|2	|teio	|4	|3.0	|0	|1
|2	|3	|nature	|4	|NaN	|0	|0


```python
pd.get_dummies(df1, columns=['sex'], drop_first=True) # firstが固定じゃないのでデータによってdropされるカテゴリ変数が変動する
```

| |id	|name	|age	|d	|sex_m
|--|--|--|--|--|--|
|0	|1	|yukino	|3	|4.0	|0
|1	|2	|teio	|4	|3.0	|1
|2	|3	|nature	|4	|NaN	|0

```python
pd.get_dummies(df1, columns=['sex'], dummy_na=True) # NaNを1つのカテゴリ変数とみなしてdummy化
```

| |id	|name	|age	|d	|sex_f	|sex_m	|sex_nan
|--|--|--|--|--|--|--|--|--|
|0	|1	|yukino	|3	|4.0	|1	|0	|0
|1	|2	|teio	|4	|3.0	|0	|1	|0
|2	|3	|nature	|4	|NaN	|0	|0	|1

機械学習ではデータが学習、テスト、CV、ホールドアウトセット、評価といった目的のために分割したり、されたりしているのが一般的。このため分割とdummy変数化のタイミングによっては次の問題が発生する。

- 登場していないカテゴリ変数がdummy化されない
- `drop_first=True`によりdropされるカテゴリ変数が登場順により変動する

2点目は`drop_first`しないことでまだなんとか防止できるにしても(線形依存性を排除するためにあとで手動でdropするとして)、問題は1点目。
例えばdf1とは別のデータセット、df2があった時、

```python
df2 = pd.DataFrame([[3,'palmer',5,'m',1],[4,'legacy',4,'s',3]], columns=df1.columns)
df2
```

| |id	|name	|age	|sex	|d
|--|--|--|--|--|--|
|0	|3	|palmer	|5	|m	|1
|1	|4	|legacy	|4	|s	|3


```python
pd.get_dummies(df2, columns=['sex'])
```

| |id	|name	|age	|d	|sex_m	|sex_s
|--|--|--|--|--|--|--|
|0	|3	|palmer	|5	|1	|1	|0
|1	|4	|legacy	|4	|3	|0	|1



df1とdf2で持ってる特徴量が変わってしまうのでいろいろと困ったことになる。というか致命的。

**使えない。**

# そんな風に考えていた時期が以下略

そうは言ってもなんとか使えないものか。他にいいやり方も見つからないし。
そこで

https://stackoverflow.com/questions/37425961/dummy-variables-when-not-all-categories-are-present/37451867#37451867

を参考に、

```python
possible_categories = ['m','f','s']
df1['sex'] = df1['sex'].astype('category', categories=possible_categories)
pd.get_dummies(df1, columns=['sex'])
```

これでいいんですね。

```console
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-159-a2d4da627942> in <module>
      1 possible_categories = ['m','f','s']
----> 2 df1['sex'] = df1['sex'].astype('category', categories=possible_categories)
      3 pd.get_dummies(df1, columns=['sex'])

~\Anaconda3\lib\site-packages\pandas\core\generic.py in astype(self, dtype, copy, errors, **kwargs)
   5880             # else, only a single dtype is given
   5881             new_data = self._data.astype(
-> 5882                 dtype=dtype, copy=copy, errors=errors, **kwargs
   5883             )
   5884             return self._constructor(new_data).__finalize__(self)

~\Anaconda3\lib\site-packages\pandas\core\internals\managers.py in astype(self, dtype, **kwargs)
    579 
    580     def astype(self, dtype, **kwargs):
--> 581         return self.apply("astype", dtype=dtype, **kwargs)
    582 
    583     def convert(self, **kwargs):

~\Anaconda3\lib\site-packages\pandas\core\internals\managers.py in apply(self, f, axes, filter, do_integrity_check, consolidate, **kwargs)
    436                     kwargs[k] = obj.reindex(b_items, axis=axis, copy=align_copy)
    437 
--> 438             applied = getattr(b, f)(**kwargs)
    439             result_blocks = _extend_blocks(applied, result_blocks)
    440 

~\Anaconda3\lib\site-packages\pandas\core\internals\blocks.py in astype(self, dtype, copy, errors, values, **kwargs)
    557 
    558     def astype(self, dtype, copy=False, errors="raise", values=None, **kwargs):
--> 559         return self._astype(dtype, copy=copy, errors=errors, values=values, **kwargs)
    560 
    561     def _astype(self, dtype, copy=False, errors="raise", values=None, **kwargs):

~\Anaconda3\lib\site-packages\pandas\core\internals\blocks.py in _astype(self, dtype, copy, errors, values, **kwargs)
    598                 if deprecated_arg in kwargs:
    599                     raise ValueError(
--> 600                         "Got an unexpected argument: {}".format(deprecated_arg)
    601                     )
    602 

ValueError: Got an unexpected argument: categories
```

！？
なんかエラー。`astype()`、`categories`ないってよ。

※ stackoverflowの記事をよく読むと最新のpandasに対応した修正がされてますね。最初に見てたコピーサイトの記事が古いままだったようです。

なので、

https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html

を参考に。

```python
from pandas.api.types import CategoricalDtype
cat_type = CategoricalDtype(categories=list('mfs'))
df1['sex'] = df1['sex'].astype(cat_type)
df1
```

| |id	|name	|age	|sex	|d
|--|--|--|--|--|--|
|0	|1	|yukino	|3	|f	|4.0
|1	|2	|teio	|4	|m	|3.0
|2	|3	|nature	|4	|NaN	|NaN

```python
pd.get_dummies(df1, columns=['sex'])
```

| |id	|name	|age	|d	|sex_m	|sex_f	|sex_s
|--|--|--|--|--|--|--|--|
|0	|1	|yukino	|3	|4.0	|0	|1	|0
|1	|2	|teio	|4	|3.0	|1	|0	|0
|2	|3	|nature	|4	|NaN	|0	|0	|0

パチパチ:clap:。(定義してないカテゴリ変数が出てきたらどうなるのかは不明 <- `astype()`を適用した段階で`NaN`に変換されちゃうっぽい)