Какие плюсы и минусы между get_dummies (Pandas) и OneHotEncoder (Scikit-learn)?

92

Я изучаю различные методы преобразования категориальных переменных в числовые для классификаторов машинного обучения. Я наткнулся на 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.dummiesсверх 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
O.rka
источник

Ответы:

56

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.
Создан
140

Вы почти наверняка захотите использовать машинное обучение 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 может быть лучше вместе с наглядным примером
Бинод Мэтьюз
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
more, чтобы опровергнуть предыдущий комментарий о том, что Sklearn OHE является второстепенным из-за handle_unknown. То же самое можно сделать с помощью pandas reindex.
Карл
При использовании get_dummies может возникнуть скрытая проблема, кроме как разового запуска. Что произойдет, если у вас есть drop_first = True, а следующий образец не включает отброшенное значение?
Mint
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, а затем также показать новые данные, которые не включают отброшенное значение.
Mint