用于相交列列表的一致 ColumnTransformer

kon*_*cov 7 python scipy pandas scikit-learn sklearn-pandas

我想以这种方式一致地使用sklearn.compose.ColumnTransformer(不是并行的,因此,第二个变换器应该仅在第一个变换器之后执行)来相交列列表:

log_transformer = p.FunctionTransformer(lambda x: np.log(x))
df = pd.DataFrame({'a': [1,2, np.NaN, 4], 'b': [1,np.NaN, 3, 4], 'c': [1 ,2, 3, 4]})
compose.ColumnTransformer(n_jobs=1,
                         transformers=[
                             ('num', impute.SimpleImputer() , ['a', 'b']),
                             ('log', log_transformer, ['b', 'c']),
                             ('scale', p.StandardScaler(), ['a', 'b', 'c'])
                         ]).fit_transform(df)
Run Code Online (Sandbox Code Playgroud)

所以,我想使用SimpleImputerfor 'a', 'b',然后logfor 'b', 'c',然后StandardScalerfor 'a', 'b', 'c'

但:

  1. 我得到形状数组(4, 7)
  2. 我仍然Nan进入a专栏b

那么,如何ColumnTransformer以以下方式用于不同的列Pipeline

更新:

pipe_1 = pipeline.Pipeline(steps=[
    ('imp', impute.SimpleImputer(strategy='constant', fill_value=42)),
])

pipe_2 = pipeline.Pipeline(steps=[
    ('imp', impute.SimpleImputer(strategy='constant', fill_value=24)),
])

pipe_3 = pipeline.Pipeline(steps=[
    ('scl', p.StandardScaler()),
])

# in the real situation I don't know exactly what cols these arrays contain, so they are not static: 
cols_1 = ['a']
cols_2 = ['b']
cols_3 = ['a', 'b', 'c']

proc = compose.ColumnTransformer(remainder='passthrough', transformers=[
    ('1', pipe_1, cols_1),
    ('2', pipe_2, cols_2),
    ('3', pipe_3, cols_3),
])
proc.fit_transform(df).T
Run Code Online (Sandbox Code Playgroud)

输出:

array([[ 1.        ,  2.        , 42.        ,  4.        ],
       [ 1.        , 24.        ,  3.        ,  4.        ],
       [-1.06904497, -0.26726124,         nan,  1.33630621],
       [-1.33630621,         nan,  0.26726124,  1.06904497],
       [-1.34164079, -0.4472136 ,  0.4472136 ,  1.34164079]])
Run Code Online (Sandbox Code Playgroud)

我明白为什么我有 cols 重复项,nans而不是缩放值,但是当 cols 不是静态时,如何以正确的方式解决这个问题?

更新2:

当列更改顺序时可能会出现问题。所以,我想用于FunctionTransformer列选择:

def select_col(X, cols=None):
    return X[cols]

ct1 = compose.make_column_transformer(
    (p.OneHotEncoder(), p.FunctionTransformer(select_col, kw_args=dict(cols=['a', 'b']))),
    remainder='passthrough'
)

ct1.fit(df)
Run Code Online (Sandbox Code Playgroud)

但得到这个输出:

ValueError:没有有效的列规范。仅允许标量、所有整数或所有字符串的列表或切片、或布尔掩码

我该如何修复它?

Ben*_*ger 5

的预期用途ColumnTransformer是并行应用不同的变压器,而不是顺序应用。为了实现您想要的结果,我想到了三种方法:

第一种方法:

pipe_a = Pipeline(steps=[('imp', SimpleImputer()),
                         ('scale', StandardScaler())])
pipe_b = Pipeline(steps=[('imp', SimpleImputer()),
                         ('log', log_transformer),
                         ('scale', StandardScaler())])
pipe_c = Pipeline(steps=[('log', log_transformer),
                         ('scale', StandardScaler())])
proc = ColumnTransformer(transformers=[
    ('a', pipe_a, ['a']),
    ('b', pipe_b, ['b']),
    ('c', pipe_c, ['c'])]
)
Run Code Online (Sandbox Code Playgroud)

第二种方法:
需要 sklearn>1.2以及引入的 pandas-out 功能。如果没有它,ColumnTransformers将重新排列列并忘记名称,这样后面的列就会失败或应用到错误的列。对于早期版本,您可以根据您的特定用例进行调整。

imp_tfm = ColumnTransformer(
    transformers=[('num', impute.SimpleImputer() , ['a', 'b'])],
    remainder='passthrough'
    )
log_tfm = ColumnTransformer(
    transformers=[('log', log_transformer, ['b', 'c'])],
    remainder='passthrough'
    )
scl_tfm = ColumnTransformer(
    transformers=[('scale', StandardScaler(), ['a', 'b', 'c'])
    )
proc = Pipeline(steps=[
    ('imp', imp_tfm),
    ('log', log_tfm),
    ('scale', scl_tfm)]
).set_output("pandas")
Run Code Online (Sandbox Code Playgroud)

第三,可能有一种方法可以使用Pipeline切片功能来拥有一个“主”管道,您可以为每个功能削减该管道...这主要像第一种方法一样工作,在管道较大的情况下可能会节省一些编码,但是看起来有点老套。例如,在这里您可以:

pipe_a = clone(pipe_b)[1:]
pipe_c = clone(pipe_b)
pipe_c.steps[1] = ('nolog', 'passthrough')
Run Code Online (Sandbox Code Playgroud)

(如果没有克隆或其他深度复制pipe_b,最后一行将同时更改pipe_cpipe_b。切片机制返回一个副本,因此pipe_a并不严格需要克隆,但我将其保留下来是为了感觉更安全。不幸的是,你不能提供不连续的切片,因此pipe_c = pipe_b[0,2]不起作用,但您可以像我上面所做的那样设置各个切片以"passthrough"禁用它们。)

  • 感谢您的回答。我已经考虑过类似的事情了。不幸的是,第一种方法在功能方面不太可扩展和自主 - 当有很多功能时,手动使用所有这些将非常困难。另外,您能否更详细地描述您的最后一句话“...可能有一种方法可以使用管道切片功能来为每个功能削减一个“主”管道...”? (2认同)