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 とか使って出来ないかなぁ。