LoginSignup
4
2

More than 3 years have passed since last update.

pandasのget_dummiesで横持ちにできないケースの対応

Last updated at Posted at 2018-11-13

2020/7/4 追記

「1つのカラムに concat されている場合」で、 sklearn 使わなくても出来たのでメモ
(2019/8/16 追記のヤツのほうが良いかもしれないけど)

結論こんな感じ
def one_hot_encoder(df, column, delimiter=','):
    # 文字列をリスト化
    tmp = df[column].str.split(delimiter)
    # 一旦縦持ち(複数の値持ってるヤツ対策)
    exploded = tmp.explode()
    # 横持ちに戻し、index毎に集計する
    return pd.get_dummies(exploded).groupby(level=0).sum()

2019/8/16 追記

2つ目のケースに関しては sklearn の MultiLabelBinarizer を使うといいんじゃない?
という情報を頂きました。なるほど、これは良さそう♪

参考:https://stackoverflow.com/a/51335634

2018/11/14 追記

本記事の読後に こちらの記事コメント をも読むことをオススメします!

はじめに

もしも以下のようなカラムを横持ちにしたい場合、pd.get_dummies()を使うと思います。

categoricay_col
a
b
a
c

でも、最近pd.get_dummies()で対応できないデータ構造のケースに出会い、
対応に割りと苦しんだので備忘として記事に残しておこうと思った次第です。

1. 複数カラムを対象に横持ちにする場合

たとえば「Qiita記事のタグデータがあり、これを横持ちにする」場合を考えます。
尚、元データは以下のようになっているとします。

  • タグ情報が tag_1, tag_2 の2カラムにまたがっている
  • タグの数は記事(行)ごとに異なる ※tag_2 が nan (NULL) の場合もある
index tag_1 tag_2
1 jupyter nan
2 python numpy
3 python anaconda
4 java nan

    ↓ (横持ち)

index python jupyter numpy anaconda java
1 0 1 0 0 0
2 1 0 1 0 0
3 1 0 0 1 0
4 0 0 0 0 1

自分はこのようなデータを以下のように横持ちにしました。

# 重複なし tag 集合をつくる
tag_1= set(df['tag_1'].dropna().unique())
tag_2= set(df['tag_2'].dropna().unique())
tag_all = sorted(tag_1.union(tag_2))

# tag を横持ち化したリスト作成
dummy = []
for t1, t2 in zip(df['tag_1'], df['tag_2']):
    tmp = []
    for ta in tag_all:
        if t1 == ta or t2 == ta :
            tmp.append(1)
        else:
            tmp.append(0)
    dummy.append(tmp)

# DataFrame型へ
df_tag = pd.DataFrame(dummy, columns=tag_all)

2. 1つのカラムに concat されている場合

1カラムに concatされている場合(情報が詰め込まれている場合)も単純に
pd.get_dummies()を出来ません。

index tag
1 jupyter
2 python,numpy
3 python,anaconda
4 java

    ↓ (横持ち)

index python jupyter numpy anaconda java
1 0 1 0 0 0
2 1 0 1 0 0
3 1 0 0 1 0
4 0 0 0 0 1

こちらも上と同じようなアプローチを取りました。

# 重複なし tag 集合をつくる
tmp = []
for row in df['tag']:
    if type(row) == str: # こうしないと nan の行でエラー
       tmp.extend(row.split(','))
tag_all = sorted(set(tmp))

# tag を横持ち化したリスト作成
dummy = []
for tag in df['tag']:
    tmp = []
    if type(tag) == str: # こうしないと nan の行でエラー
        tag_list = tag.split(',')
        for ta in tag_all:
            if ta in tag_list:
                tmp.append(1)
            else:
                tmp.append(0)
    else:
        tmp = [0 for i in tag_all] # nan の場合は全要素が 0 のリストを返す
    dummy.append(tmp)

# DataFrame型へ
df_tag = pd.DataFrame(dummy, columns=tag_all)

感想

本当にただの力技です。for でぶん回しただけです。
動かしてみるとわかりますが、結構処理時間もかかります。。。
高速化したいなぁ。

あと、せっかく pandas 使ってるんだから apply, map とか使って出来ないかなぁ。

4
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2