Was sind die Vor- und Nachteile zwischen get_dummies (Pandas) und OneHotEncoder (Scikit-learn)?

84

Ich lerne verschiedene Methoden, um kategoriale Variablen für maschinell lernende Klassifikatoren in numerische umzuwandeln. Ich bin auf die pd.get_dummiesMethode gestoßen sklearn.preprocessing.OneHotEncoder()und wollte sehen, wie sie sich in Bezug auf Leistung und Nutzung unterscheiden.

Ich habe seitdem ein Tutorial zur Verwendung OneHotEncoder()unter https://xgdgsc.wordpress.com/2015/03/20/note-on-using-onehotencoder-in-scikit-learn-to-work-on-categorical-features/ gefunden Die sklearnDokumentation war für diese Funktion nicht allzu hilfreich. Ich habe das Gefühl, ich mache es nicht richtig ... aber

Können einige die Vor- und Nachteile der Verwendung von pd.dummiesOver erklären sklearn.preprocessing.OneHotEncoder()und umgekehrt? Ich weiß, dass OneHotEncoder()Sie eine spärliche Matrix erhalten, aber ansonsten bin ich mir nicht sicher, wie sie verwendet wird und welche Vorteile sie gegenüber der pandasMethode haben. Benutze ich es ineffizient?

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
quelle

Antworten:

55

OneHotEncoderZeichenfolgenwerte können nicht direkt verarbeitet werden. Wenn Ihre nominalen Features Zeichenfolgen sind, müssen Sie sie zuerst in Ganzzahlen abbilden.

pandas.get_dummiesist irgendwie das Gegenteil. Standardmäßig werden nur Zeichenfolgenspalten in eine One-Hot-Darstellung konvertiert, sofern keine Spalten angegeben sind.

nr
quelle
Hey @nos, entschuldigen Sie die Verzögerung bei der Beantwortung dieser Antwort
O.rka
1
Ist eines effizient gegenüber dem anderen?
Ankit Seth
6
Update, OneHotEncoderkann in der Version 0.20.0 nicht auch auf Strings angewendet werden.
Bs He
15
@BsHe Nicht mehr wahr in sklearn 0.20.3: OneHotEncoder(sparse=False).fit_transform(pd.DataFrame(pd.Series(['good','bad','worst','good', 'good', 'bad'])))funktioniert, was bedeutet, OneHotEncoderdass auf stirngs angewendet werden kann.
Dzieciou
1
Sie können keine neuen unsichtbaren Daten mit codieren pd.get_dummies.
Gented
127

Für maschinelles Lernen möchten Sie fast definitiv verwenden sklearn.OneHotEncoder. Für andere Aufgaben wie einfache Analysen können Sie diese möglicherweise verwenden pd.get_dummies, was etwas bequemer ist.

Beachten Sie, dass sklearn.OneHotEncoderdies in der neuesten Version aktualisiert wurde, sodass Zeichenfolgen für kategoriale Variablen sowie Ganzzahlen akzeptiert werden .

Der springende Punkt dabei ist, dass der sklearnEncoder eine Funktion erstellt, die bestehen bleibt und dann auf neue Datensätze angewendet werden kann, die dieselben kategorialen Variablen verwenden, mit konsistenten Ergebnissen .

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)

Beachten Sie, wie wir denselben Encoder, über X_trainden wir erstellt haben, auf den neuen Datensatz anwenden X_test.

Überlegen Sie, was passiert, wenn X_testandere Ebenen als X_trainfür eine der Variablen enthalten sind. Nehmen wir zum Beispiel an, X_train["color"]enthält nur "red"und "green", aber zusätzlich zu diesen, X_test["color"]enthält manchmal "blue".

Wenn wir verwenden pd.get_dummies, X_testwird eine zusätzliche "color_blue"Spalte angezeigt, die X_trainnicht vorhanden ist, und die Inkonsistenz wird wahrscheinlich später unseren Code beschädigen, insbesondere wenn wir X_testein sklearnModell füttern, auf dem wir trainiert haben X_train.

Und wenn wir die Daten so in der Produktion verarbeiten möchten, wo wir jeweils nur ein Beispiel erhalten, pd.get_dummiesist dies nicht von Nutzen.

Mit sklearn.OneHotEncoderauf der anderen Seite, wenn wir den Encoder erstellt haben, können wir es wieder verwenden die gleiche Leistung jedes Mal zu erzeugen, mit Spalten nur für "red"und "green". Und wir können explizit steuern, was passiert, wenn es auf das neue Level trifft "blue": Wenn wir denken, dass dies unmöglich ist, können wir es anweisen, einen Fehler mit zu werfen handle_unknown="error"; Andernfalls können wir es anweisen, fortzufahren, und einfach die roten und grünen Spalten mit auf 0 setzen handle_unknown="ignore".

Denziloe
quelle
22
Ich glaube, diese Antwort hat weitaus größere Auswirkungen als die akzeptierte. Die wahre Magie besteht darin, mit unbekannten kategorialen Merkmalen umzugehen, die in der Produktion auftauchen werden.
Marktschreier
2
Ich denke, dies ist eine bessere, vollständigere Antwort als die akzeptierte Antwort.
Chiraz BenAbdelkader
1
Ja. IMHO, das ist eine bessere Antwort als die akzeptierte Antwort.
dami.max
1
Jep . Diese Antwort erklärt definitiv besser, warum one_hot_encoder besser sein könnte, zusammen mit einem klaren Beispiel
Binod Mathews
1
Das war eine schöne Erklärung. Ein
Hypnos
4

Warum sollten Sie die Spalten nicht einfach als Variable col_list aus den resultierenden get_dummies zwischenspeichern oder speichern und dann mit pd.reindex den Zug gegen Testdatensätze ausrichten? Beispiel:

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) 
Carl
quelle
Wie beantwortet dies die Frage?
Jorijnsmit
mehr, um den vorherigen Kommentar zu widerlegen, dass Sklearn OHE wegen handle_unknown besser ist. Das gleiche kann mit Pandas Reindex erreicht werden.
Carl
Es kann ein hinterhältiges Problem bei der Verwendung von get_dummies geben, außer als einmaliger Lauf. Was passiert, wenn Sie drop_first = True haben und das nächste Beispiel den abgelegten Wert nicht enthält?
Münze
2

Ich mag Carls Antwort sehr und habe sie positiv bewertet. Ich werde nur Carls Beispiel ein wenig erweitern, damit mehr Leute hoffentlich zu schätzen wissen, dass pd.get_dummies mit Unbekanntem umgehen kann. Die beiden folgenden Beispiele zeigen, dass pd.get_dummies bei der Handhabung von Unbekanntem wie OHE dasselbe erreichen kann.

# 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']))

Verwenden von 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

Verwenden von 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.]])
Sarah
quelle
Können Sie Ihre Antwort bitte um ein Beispiel mit drop_first = True erweitern und dann auch neue Daten anzeigen, die den abgelegten Wert nicht enthalten?
Münze