Как реализовать one-hot кодирование?
Имею фреймы данных new_df и df_train1 соответственно:
a d
Id
0 NaN NaN
1 NaN NaN
2 NaN NaN
target1
0 [a, b, c]
1 [a]
2 [b, d]
Я хотел бы пробегать по именам колонок new_df, смотреть есть ли это значение в элементах (списках) df_train1 и ставить 1 или 0 вместо NaN на соответствующей позиции.
В результате я хотел бы получить фрейм new_df следующего вида, закодировав столбцы в виде one-hot векторов:
a d
Id
0 1 0
1 1 0
2 0 1
На данный момент я имею следующий код с неверным выводом:
new_df = pd.DataFrame(columns=['a','d'])
new_df.insert(loc=0, column='Id', value=np.arange(0,3,1))
new_df = new_df.set_index('Id')
df_train1 = pd.DataFrame({'target1':[['a','b','c'],['a'],['b','d']]})
for word in new_df.columns:
for i in new_df.index:
if list(word) in list(df_train1.loc[df_train1.index==i,'target1']):
new_df.at[i,word]=1
else:
new_df.at[i,word]=0
new_df
a d
Id
0 0 0
1 1 0
2 0 0
Подскажите, в чем заключается ошибка?
Возможно есть более простые пути для реализации такого кодирования, или же другое, более подходящее кодирование для подобных задач с большим количеством данных? В моем реальном датасете 16290 объектов и 1264 признаков (колонок new_df). Этот код выполнялся 3+ часа.
Ответы (2 шт):
я бы предложил использовать векторизованные методы, например так:
tmp = pd.get_dummies(df_train1.explode('target1'), prefix='', prefix_sep='')
res = tmp.groupby(tmp.index).sum()[new_df.columns]
теперь res:
a d
0 1 0
1 1 0
2 0 1
Если по условию у вас в списках датафрейма target1 могут быть повторы, то правильнее будет сделать так:
res = tmp.groupby(tmp.index).any().astype(int)[new_df.columns]
Можно воспользоваться специально созданным для этого классом sklearn.preprocessing.MultiLabelBinarizer:
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer(classes=["a", "d"])
res = pd.DataFrame(
mlb.fit_transform(df_train1["target1"]),
index=df_train1.index,
columns=mlb.classes_
)
результат:
In [36]: res
Out[36]:
a d
0 1 0
1 1 0
2 0 1