使用SimpleImputer和OneHotEncode的管道-如何正确使用?

2022-04-09 00:00:00 python scikit-learn pipeline

问题描述

我面临的挑战是创建一个管道来计算(SI)一个类别变量(如颜色),然后输入(OHE)2个变量(如颜色和星期几)。在两个步骤中使用了颜色。

我想将SI和OHE放在1 ColumnTransformer中。我刚刚了解到SI和OHE是并行运行的,这意味着OHE不会对推算的颜色进行编码。

然后我尝试:

si = SimpleImputer(strategy='mean', add_indicator=True)
ohe = OneHotEncoder(sparse=False,drop='first')

ctsi  = ColumnTransformer(transformers=[('si',si,['colour'])],remainder='passthrough')
ctohe = ColumnTransformer(transformers=[('ohe',ohe,['colour','dayofweek'])],remainder='passthrough')

pl = Pipeline([('ctsi',ctsi),('ctohe',ctohe)])

outfit = pl.fit_transform(X,y)

我收到错误:

ValueError: Specifying the columns using strings is only supported for pandas DataFrames

我相信是因为列名COLOR已被SI删除。当我将OHE列更改为INT列表时:

ctohe = ColumnTransformer(transformers=[('ohe',ohe,[0,1])],remainder='passthrough')

它通过了。我只是在测试处理,很明显,列是不正确的。

所以我在这里的挑战是,考虑到我想要实现的目标,这是可能的吗?我如何才能做到这一点?

非常感谢!


解决方案

实际上,我同意你的推理。这个问题是因为ColumnTransformer在转换之后忘记了列名,而实际上-引用the answer in here-ColumnTransformer的目的是处理并行应用的转换。我认为doc中也有这样的规定:

此估计器允许输入的单独转换。[.]这对于异类或柱状数据将多个特征提取机制或转换合并到单个转换器非常有用。

我猜解决这个问题的一种方法可能是对列进行自定义重命名,根据需要将Callable传递给ColumnTransformertransformers元组(name, transformer, columns)(文档后面的表示法)的columns部分(实际上,我猜如果您将可调用的ColumnTransformer传递给管道中的第二个ColumnTransformer实例就可以了)。 编辑:我必须以某种方式撤回我所写的内容,我实际上不确定将可调用函数传递给列是否适合您的需要,因为您的问题并不是列选择本身,而是通过字符串列名称进行的列选择,对于这一点,您需要一个DataFrame(而IMO仅作用于列选择器不会解决此类问题)。

相反,您可能更好地需要一个转换器,在作用于DataFrame的ColumnTransformer的不同实例必须在Pipeline中按顺序转换相同的变量时,以某种方式更改之后的和之前的列名(仍然假设该设置不是理想的设置)。

实际上,几个月前,以下https://github.com/scikit-learn/scikit-learn/pull/21078被合并了;我怀疑它不是最新版本,因为通过升级sklearn,我无法使其工作。无论如何,IMO在将来可能会缓解类似的情况,因为它将get_feature_names_out()添加到SimpleImputer,而get_feature_names_out()在处理列名时又非常有用。

总的来说,我还建议the same post linked above了解更多详细信息。

最终,我可以找到一个简单的例子;它是不可伸缩的(我试图利用匹配的SimpleImputer实例的feature_names_in_属性获得更具伸缩性的东西,但没有得到一致的结果),但希望能给出一些提示。

import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin

X = pd.DataFrame({'city': ['London', 'London', 'Paris', np.NaN],
          'title': ['His Last Bow', 'How Watson Learned the Trick', 'A Moveable Feast', 'The Grapes of Wrath'],
          'expert_rating': [5, 3, 4, 5],
          'user_rating': [4, 5, 4, 3]})

ct_1 = ColumnTransformer([('si', SimpleImputer(strategy='most_frequent'), ['city'])],
              remainder='passthrough')
ct_2 = ColumnTransformer([('ohe', OneHotEncoder(), ['city'])], remainder='passthrough', verbose_feature_names_out=True)

class ColumnExtractor(BaseEstimator, TransformerMixin):
    def __init__(self, columns):
        self.columns = columns

    def transform(self, X, *_):
        return pd.DataFrame(X, columns=self.columns)

    def fit(self, *_):
        return self

pipe = Pipeline([
    ('ct_1', ct_1),
    ('ce', ColumnExtractor(['city', 'title', 'expert_rating', 'user_rating'])),
    ('ct_2', ct_2)
])

pipe.fit_transform(X)

相关文章