使用 SimpleImputer 和 OneHotEncoder 的管道 - 如何正确执行?

Pipeline with SimpleImputer and OneHotEncoder - how to do properly?

我面临着创建管道来估算 (SI) 类别变量(例如颜色)然后 onehotencode (OHE) 2 个变量(例如颜色和星期几)的挑战。颜色用于 2 个步骤。

我想将 SI 和 OHE 放在 1 个 ColumnTransformer 中。我刚刚了解到 SI 和 OHE 运行 是并行的,这意味着 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

我相信这是因为列名颜色已被 SI 删除。当我将 OHE 列更改为 int 列表时:

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

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

所以我的挑战是考虑到我想要完成的事情,这可能吗?我该怎么做?

非常感谢!

其实我同意你的推理。问题是 ColumnTransformer 忘记了转换后的列名,事实上 - 在我看来通过这句话引用 the answer in here - ColumnTransformer's intended usage is to deal with transformations applied in parallel. That's also specified in the doc

This estimator allows different columns or column subsets of the input to be transformed separately. [...] This is useful for heterogeneous or columnar data, to combine several feature extraction mechanisms or transformations into a single transformer.

我想对此的一种解决方案可能是对列进行自定义重命名,将 callable 传递给 [=11= 的 columns 部分] 的 transformers 元组 (name, transformer, columns) (文档后面的符号)根据您的需要(实际上,如果您将可调用对象传递给管道中的第二个 ColumnTransformer 实例,我想这会起作用). 编辑:我不得不以某种方式撤回我写的东西,我实际上不确定将可调用项传递给列是否可以满足您的需要问题本身并不真正存在于列选择中,而是存在于通过字符串列名进行的列选择中,为此您需要一个 DataFrame(并且仅作用于列选择器的 imo 无法解决此类问题)。

相反,您可能更好地需要一个变换器,它以某种方式更改列名在插补之后和在单热编码之前(仍然假设当 ColumnTransformer 的不同实例必须在作用于 DataFrame 的 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)

我认为最简单的选择(目前)是两列的单独管道:

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

siohe = Pipeline([
    ('si', si),
    ('ohe', ohe),
])

ct = ColumnTransformer(
    transformers=[
        ('si', siohe, ['colour']),
        ('siohe', ohe, ['dayofweek']),
    ],
    remainder='passthrough'
)

(您将策略指定为 'mean',这可能不是您想要的,但我已将其留在上面的代码中。)

您也可以考虑对两列都使用 siohe:如果 dayofweek(在生产数据中!)没有缺失,那么就没有真正的区别。