使用 sklearn OneHotEncoder 时如何去掉数字列?

Posted

技术标签:

【中文标题】使用 sklearn OneHotEncoder 时如何去掉数字列?【英文标题】:How to leave numerical columns out when using sklearn OneHotEncoder? 【发布时间】:2020-07-02 21:18:06 【问题描述】:

环境:

import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import RandomForestClassifier

样本数据:

X_train = pd.DataFrame('A': ['a1', 'a3', 'a2'], 
                        'B': ['b2', 'b1', 'b3'],
                        'C': [1, 2, 3])
y_train = pd.DataFrame('Y': [1,0,1])

期望的结果: 我想以这种格式在我的管道中包含 sklearn OneHotEncoder:

encoder = ### SOME CODE ###
scaler = StandardScaler()
model = RandomForestClassifier(random_state=0)

# This is my ideal pipeline
pipe = Pipeline([('OneHotEncoder', encoder),
                 ('Scaler', scaler),
                 ('Classifier', model)])
pipe.fit(X_train, y_train)

挑战: OneHotEncoder 正在对包括数字列在内的所有内容进行编码。我想保持数字列不变,并以与 Pipeline() 兼容的有效方式仅对分类特征进行编码。

encoder = OneHotEncoder(drop='first', sparse=False) 
encoder.fit(X_train)
encoder.transform(X_train) # Columns C is encoded - this is what I want to avoid

解决方法(不理想):我可以使用 pd.get_dummies() 解决问题。但是,这意味着我不能将它包含在我的管道中。或者有什么办法?

X_train = pd.get_dummies(X_train, drop_first=True)

【问题讨论】:

【参考方案1】:

我要做的是创建自己的自定义转换器并将其放入管道中。通过这种方式,您将对手中的数据拥有很大的权力。所以,步骤如下:

1) 创建一个继承BaseEstimator 和TransformerMixin 的自定义转换器类。在其transform() 函数中尝试检测该列的值是数字还是分类。如果您现在不想处理逻辑,您可以随时将分类列的列名提供给您的 transform() 函数以动态选择。

2)(可选)创建您的自定义转换器以处理仅具有分类值的列。

3)(可选)创建您的自定义转换器以处理仅包含数值的列。

4) 使用您创建的转换器构建两个管道(一个用于分类,另一个用于数字),您还可以使用 sklearn 中现有的。

5) 用FeatureUnion 合并两个管道。

6) 将您的大型管道与您的 ML 模型合并。

7) 致电fit_transform()

示例代码(未实现任何选项):GitHub Jupyter Noteboook

【讨论】:

谢谢 Seleme,是否可以使用我提供的示例数据包含示例代码来说明您的意思? @ZolzayaLuvsandorj 我添加了一个 jupyter notebook 链接。在那里,您可以看到转换后的数据集。观察AB 列是OneHotEncode'd 而C 列是StandardScale'd @ZolzayaLuvsandorj 您必须添加独特的dtypes,这些dtypes 定义在numpy 中,可能存在于您的DataFrame 中。 dict _supported_dtypes 负责映射分类和数字数据类型。 @ZolzayaLuvsandorj 忘记添加代码,但您显然可以在转换后的X_train 上使用模型。 感谢@Seleme 的贡献,我看到你推荐的一种热编码管道,看起来很复杂。牢记我想要的结果,我如何将 FeatureUnion 与我的整个管道结合起来?因为我想要一个管道,其中包括一个用于分类变量、缩放器和分类器的热编码。【参考方案2】:

我首选的解决方案是使用 sklearn 的 ColumnTransformer(参见 here)。

它使您可以根据需要将数据分成任意数量的组(在您的情况下,分类数据与数值数据),并对这些组应用不同的预处理操作。然后,该转换器可以在管道中用作任何其他 sklearn 预处理工具。这是一个简短的例子:

import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier

X = pd.DataFrame("a":[1,2,3],"b":["A","A","B"])
y = np.array([0,1,1])

OHE = OneHotEncoder()
scaler = StandardScaler()
RFC = RandomForestClassifier()

cat_cols = ["b"]
num_cols = ["a"]

transformer = ColumnTransformer([('cat_cols', OHE, cat_cols),
                                ('num_cols', scaler, num_cols)])

pipe = Pipeline([("preprocessing", transformer),
                ("classifier", RFC)])
pipe.fit(X,y)

注意:我已根据您的请求获得了一些许可,因为这仅将缩放器应用于数字数据,我认为这更有意义吗?如果您确实想将缩放器应用于所有列,也可以通过修改此示例来实现。

【讨论】:

感谢您向我介绍 ColumnTransformer。缩放器的好点。如果我使用的是 MinMaxScaler(),我同意。然而,使用 StandardScaler(),一个热编码的假人仍然会被转换,以便它们的平均值以 0 为中心。如果我使用对特征尺度更敏感的建模技术,你对标准化假人和数字特征有什么想法,比如逻辑回归? 如果我仍然想标准化一切,这是调整代码最有效的方法:transformer = ColumnTransformer([('cat_cols', OHE, cat_cols)], remainder = 'passthrough') 然后pipe = Pipeline([("preprocessing", transformer), ("scaling", scaler), ("classifier", RFC)]) MinMaxScaler 不会影响您的一个热门编码列。另一方面,StandardScaler 可以。我仍然认为缩放这些没有直观意义,但您仍然可以尝试看看它如何影响您的分类分数。对于您的第二个问题,是的,这就是您将如何调整我的示例以将缩放器应用于所有功能。

以上是关于使用 sklearn OneHotEncoder 时如何去掉数字列?的主要内容,如果未能解决你的问题,请参考以下文章

sklearn:无法使 OneHotEncoder 与 Pipeline 一起使用

sklearn中的LabelEncoder和OneHotEncoder的区别

在 sklearn 0.14 中使用 OneHotEncoder 指定要分类的选择特征

sklearn.preprocessing.OneHotEncoder

使用 sklearn OneHotEncoder 时如何去掉数字列?

onehotencoder 的 sklearn 掩码不起作用