Які плюси та мінуси між get_dummies (Pandas) та OneHotEncoder (Scikit-learn)?


93

Я вивчаю різні методи перетворення категоріальних змінних у числові для класифікаторів машинного навчання. Я натрапив на pd.get_dummiesметод, і sklearn.preprocessing.OneHotEncoder()я хотів побачити, як вони відрізняються з точки зору продуктивності та використання.

Я знайшов підручник з використання OneHotEncoder()на https://xgdgsc.wordpress.com/2015/03/20/note-on-using-onehotencoder-in-scikit-learn-to-work-on-categorical-features/, оскільки sklearnдокументація була не дуже корисно на цій функції. У мене таке відчуття, що я роблю це не правильно ... але

Чи можуть хтось пояснити плюси і мінуси використання pd.dummiesover sklearn.preprocessing.OneHotEncoder()і навпаки? Я знаю, що це OneHotEncoder()дає розріджену матрицю, але крім того, що я не впевнений, як вона використовується і які переваги від pandasметоду. Я використовую його неефективно?

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
sns.set()

%matplotlib inline

#Iris Plot
iris = load_iris()
n_samples, m_features = iris.data.shape

#Load Data
X, y = iris.data, iris.target
D_target_dummy = dict(zip(np.arange(iris.target_names.shape[0]), iris.target_names))

DF_data = pd.DataFrame(X,columns=iris.feature_names)
DF_data["target"] = pd.Series(y).map(D_target_dummy)
#sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  \
#0                  5.1               3.5                1.4               0.2   
#1                  4.9               3.0                1.4               0.2   
#2                  4.7               3.2                1.3               0.2   
#3                  4.6               3.1                1.5               0.2   
#4                  5.0               3.6                1.4               0.2   
#5                  5.4               3.9                1.7               0.4   

DF_dummies = pd.get_dummies(DF_data["target"])
#setosa  versicolor  virginica
#0         1           0          0
#1         1           0          0
#2         1           0          0
#3         1           0          0
#4         1           0          0
#5         1           0          0

from sklearn.preprocessing import OneHotEncoder, LabelEncoder
def f1(DF_data):
    Enc_ohe, Enc_label = OneHotEncoder(), LabelEncoder()
    DF_data["Dummies"] = Enc_label.fit_transform(DF_data["target"])
    DF_dummies2 = pd.DataFrame(Enc_ohe.fit_transform(DF_data[["Dummies"]]).todense(), columns = Enc_label.classes_)
    return(DF_dummies2)

%timeit pd.get_dummies(DF_data["target"])
#1000 loops, best of 3: 777 µs per loop

%timeit f1(DF_data)
#100 loops, best of 3: 2.91 ms per loop

Відповіді:


57

OneHotEncoderне може безпосередньо обробляти значення рядків. Якщо ваші номінальні ознаки - це рядки, то вам потрібно спочатку зіставити їх у цілі числа.

pandas.get_dummiesє якимось протилежним. За замовчуванням він перетворює лише рядкові стовпці в одне гаряче представлення, якщо не вказані стовпці.


Привіт @nos, вибачте за затримку зв’язку з вами щодо цієї відповіді
O.rka

1
Крім цього, чи є один ефективний над іншим?
Анкіт Сет,

6
оновлення, OneHotEncoderне можна застосувати і до рядків у версії 0.20.0.
Bs He

15
@BsHe Більше не відповідає дійсності у sklearn 0.20.3: OneHotEncoder(sparse=False).fit_transform(pd.DataFrame(pd.Series(['good','bad','worst','good', 'good', 'bad'])))працює, що означає, що OneHotEncoderїї можна застосовувати на штангах.
dzieciou

1
Ви не можете кодувати нові невидимі дані за допомогою pd.get_dummies.
генеровано

142

Для машинного навчання ви майже напевно хочете використовувати sklearn.OneHotEncoder. Для інших завдань, таких як прості аналізи, ви можете скористатися pd.get_dummies, що трохи зручніше.

Зверніть увагу, що sklearn.OneHotEncoderоновлена ​​в останній версії, так що вона приймає рядки для категоріальних змінних, а також цілі числа.

Суть цього полягає в тому, що sklearnкодер створює функцію, яка зберігається, а потім може бути застосована до нових наборів даних, що використовують ті самі категоріальні змінні, з незмінними результатами .

from sklearn.preprocessing import OneHotEncoder

# Create the encoder.
encoder = OneHotEncoder(handle_unknown="ignore")
encoder.fit(X_train)    # Assume for simplicity all features are categorical.

# Apply the encoder.
X_train = encoder.transform(X_train)
X_test = encoder.transform(X_test)

Зверніть увагу, як ми застосовуємо той самий кодер, який ми створили, X_trainдо нового набору даних X_test.

Поміркуйте, що трапиться, якщо X_testмістить різні рівні, ніж X_trainдля однієї з його змінних. Наприклад, скажімо, X_train["color"]містить лише "red"і "green", але, крім цих, X_test["color"]іноді містить "blue".

Якщо ми використовуємо pd.get_dummies, X_testв кінцевому підсумку з’явиться додатковий "color_blue"стовпець, якого X_trainнемає, і невідповідність, можливо, згодом порушить наш код, особливо якщо ми переходимо X_testдо sklearnмоделі, яку ми навчали X_train.

І якщо ми хочемо обробляти подібні дані у виробництві, де ми отримуємо по одному прикладу за раз, pd.get_dummiesне буде корисно.

З sklearn.OneHotEncoderіншого боку, після того, як ми створили кодер, ми можемо використовувати його повторно, щоб видавати однакові результати кожного разу, із стовпцями лише для "red"та "green". І ми можемо чітко контролювати, що відбувається, коли він переходить на новий рівень "blue": якщо ми вважаємо, що це неможливо, тоді ми можемо сказати йому, що він повинен помилку handle_unknown="error"; в іншому випадку ми можемо сказати йому продовжувати і просто встановити червоний і зелений стовпці на 0, за допомогоюhandle_unknown="ignore" .


23
Я вважаю, що ця відповідь має набагато більший вплив, ніж прийнята. Справжня магія - це обробка невідомих категоріальних особливостей, які неодмінно з’являться у виробництві.
Баркер

2
Я думаю, що це краща, більш повна відповідь, ніж прийнята відповідь.
Chiraz BenAbdelkader

1
Так. ІМХО, це краща відповідь, ніж прийнята відповідь.
dami.max

1
Так . Ця відповідь, безумовно, краще пояснює, чому one_hot_encoder може бути кращим разом із наочним прикладом
Binod Mathews

1
Це було прекрасне пояснення.
Слава

4

чому б вам просто не кешувати або не зберігати стовпці як змінну col_list з отриманих get_dummies, а потім використовувати pd.reindex для вирівнювання набору поїздів проти тестових наборів даних .... приклад:

df = pd.get_dummies(data)
col_list = df.columns.tolist()

new_df = pd.get_dummies(new_data)
new_df = new_df.reindex(columns=col_list).fillna(0.00) 

Як це відповідає на питання?
jorijnsmit

більше, щоб спростувати попередній коментар про те, що Sklearn OHE є вищим через handle_unknown. Те саме можна зробити за допомогою переіндексації панд.
Карл,

З використанням get_dummies може бути підла проблема, за винятком одноразового запуску. Що станеться, якщо у вас є drop_first = True, а наступний зразок не включає впущене значення?
Монетний двір

3

Мені дуже подобається відповідь Карла і я її підтримав. Я просто трохи розширю приклад Карла, щоб більше людей, сподіваємось, зрозуміли, що pd.get_dummies може обробляти невідоме. Два наведені нижче приклади показують, що pd.get_dummies може виконати те саме, обробляючи невідоме, як OHE.

# data is from @dzieciou's comment above
>>> data =pd.DataFrame(pd.Series(['good','bad','worst','good', 'good', 'bad']))
# new_data has two values that data does not have. 
>>> new_data= pd.DataFrame(
pd.Series(['good','bad','worst','good', 'good', 'bad','excellent', 'perfect']))

Використання pd.get_dummies

>>> df = pd.get_dummies(data)
>>> col_list = df.columns.tolist()
>>> print(df)
   0_bad  0_good  0_worst
0      0       1        0
1      1       0        0
2      0       0        1
3      0       1        0
4      0       1        0
5      1       0        0
6      0       0        0
7      0       0        0

>>> new_df = pd.get_dummies(new_data)
# handle unknow by using .reindex and .fillna()
>>> new_df = new_df.reindex(columns=col_list).fillna(0.00)
>>> print(new_df)
#    0_bad  0_good  0_worst
# 0      0       1        0
# 1      1       0        0
# 2      0       0        1
# 3      0       1        0
# 4      0       1        0
# 5      1       0        0
# 6      0       0        0
# 7      0       0        0

Використання OneHotEncoder

>>> encoder = OneHotEncoder(handle_unknown="ignore", sparse=False)
>>> encoder.fit(data)
>>> encoder.transform(new_data)
# array([[0., 1., 0.],
#        [1., 0., 0.],
#        [0., 0., 1.],
#        [0., 1., 0.],
#        [0., 1., 0.],
#        [1., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.]])

Чи можете ви, будь ласка, розширити свою відповідь, включивши приклад із drop_first = True, а потім також показати нові дані, які не включають скинуте значення.
Монетний двір
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.