get_dummies(Pandas)和OneHotEncoder(Scikit-learn)之间的优缺点是什么?

144

我正在学习将分类变量转换为数字以用于机器学习分类器的不同方法。我发现了 pd.get_dummies 方法和 sklearn.preprocessing.OneHotEncoder(),想看看它们在性能和使用方面的区别。

我找到了一个关于如何在https://xgdgsc.wordpress.com/2015/03/20/note-on-using-onehotencoder-in-scikit-learn-to-work-on-categorical-features/ 上使用 OneHotEncoder() 的教程,因为 sklearn 文档在这个特性上并不是很有帮助。我感觉我可能没有正确地使用它...

有人可以解释一下使用 pd.dummiessklearn.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
5个回答

246

对于机器学习,您几乎肯定想要使用 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_dummiesX_test将以多出一个"color_blue"列的形式结束,而X_train没有这个列,这种不一致可能会在以后破坏我们的代码,尤其是如果我们将X_test馈送到在X_train上进行训练的sklearn模型中。

而且,如果我们希望在生产中这样处理数据,即每次只接收一个示例,那么pd.get_dummies将无法使用。

另一方面,使用sklearn.OneHotEncoder,一旦我们创建了编码器,我们可以重复使用它来生成相同的输出,只包含"red""green"列。并且我们可以明确地控制当遇到新级别"blue"时会发生什么:如果我们认为这是不可能的,那么我们可以告诉它用handle_unknown="error"抛出错误;否则,我们可以告诉它继续并将红色和绿色列设为0,使用handle_unknown="ignore"


41
我相信这个答案的影响要比被采纳的答案更大。真正的奥妙在于处理那些在生产中必然会出现的未知分类特征。 - barker
5
我认为这个回答比被采纳的回答更好、更完整。 - Chiraz BenAbdelkader
1
是的。在我看来,这个答案比被接受的答案更好。 - dami.max
1
是的。这个答案明确地解释了为什么one_hot_encoder可能更好,并且提供了一个清晰的例子。 - Binod Mathews
@ErnestoLopezFune 我认为我们可以通过填充或使用管道最简单地处理OneHotEncoder之前的分类变量缺失值。#分类数据预处理 categorical_transformer = Pipeline(steps=[ ('imputer',SimpleImputer(strategy ='most_frequent')), ('onehot',OneHotEncoder(handle_unknown ='ignore')) ]它将用“most_frequent”值替换。 - code_conundrum
显示剩余2条评论

71

OneHotEncoder无法直接处理字符串值。如果您的名义特征是字符串,则需要先将它们映射为整数。

pandas.get_dummies则相反,除非指定列,否则默认只会将字符串列转换为一键编码表示。


1
除此之外,有一个比另一个更有效吗? - Ankit Seth
6
更新:在0.20.0版本中,OneHotEncoder不能用于字符串。 - Bs He
20
在sklearn 0.20.3中已不再适用:OneHotEncoder(sparse=False).fit_transform(pd.DataFrame(pd.Series(['good','bad','worst','good', 'good', 'bad']))) 可以正常运行,这意味着 OneHotEncoder 可以应用于字符串。 - dzieciou
@dzieciou 适合更新。 - Bs He
10
pd.get_dummies不能对新的未见过的数据进行编码。 - gented

9

我非常喜欢Carl的答案并点赞了。我将扩展一下Carl的例子,以便更多人能够理解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.]])

1
请问您能否扩展您的回答,包括使用drop_first = True的示例,并展示不包括已删除的值的新数据。 - Mint

6
为什么不直接将结果的get_dummies缓存或保存为变量col_list,然后使用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) 

这个回答如何解决问题? - gosuto
更多地反驳之前的评论,即Sklearn OHE由于能够处理未知数据而更加优越。使用pandas reindex 也可以实现相同效果。 - Carl
仅使用get_dummies一次可能会存在一个难以察觉的问题。如果您设置了drop_first=True,并且下一个样本不包含已删除的值,会发生什么? - Mint

0

这个问题早在很久以前就被提出了,但在2023年仍然具有相关性。

简而言之:两者都可以用于任务,选择哪一个取决于个人偏好和其他情况。

稍微详细一些:

  • 对于OneHotEncoder和get_dummies,最可靠的方法是显式指定类别。对于OneHotEncoder,可以使用“categories”参数来实现,该参数是一个列表的列表。对于get_dummies,您需要将相关列转换为具有适当类别的分类变量。

  • OneHotEncoder假设您想要编码数据中的所有列,因此如果不是这种情况,则必须手动选择/转换/与原始列连接或将OneHotEncoder包装在列转换器中。使用get_dummies更容易。

  • 如果您喜欢在数据处理管道期间保持在DataFrame空间中,则pandas.get_dummies是最直接的方法,但如果您依赖于scikit Pipeline-s,则包装在列转换器中的OneHotEncoder更为直接。

如需详细解释和示例,请阅读我的Towards Data Science文章。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接