在管道中堆叠输入器

Stacking up imputers in a pipeline

我有一个关于在管道中堆叠多个 sklearn SimpleImputers 的问题:

import numpy as np
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer  

pipeline = Pipeline([
    ('si1', SimpleImputer(missing_values = np.nan,
                          strategy='constant',
                          fill_value=-1)),
    ('si2', SimpleImputer(missing_values = None,
                          strategy='constant',
                          fill_value=-1))
])

train = pd.DataFrame({'f1': [True, 1, 0], 'f2': [None,None,None]})
test1 = pd.DataFrame({'f1': [0, False, 0], 'f2': [np.nan, np.nan, np.nan]})
test2 = pd.DataFrame({'f1': [0, 0, 0], 'f2': [np.nan, np.nan, np.nan]})

pipeline.fit_transform(train)
pipeline.transform(test1)
pipeline.transform(test2)

该代码对于转换 test1(包含一个布尔值)工作正常,但对于 test2 失败:

ValueError: 'X' and 'missing_values' types are expected to be both numerical. Got X.dtype=float64 and type(missing_values)=<class 'NoneType'>.

显然,在存在字符串或布尔值的情况下,转换工作正常,但在只有数值时会失败。

另一个奇怪的行为是当我在管道内切换输入器的顺序时:

pipeline = Pipeline([
    ('si2', SimpleImputer(missing_values = None,
                          strategy='constant',
                          fill_value=-1)),
    ('si1', SimpleImputer(missing_values = np.nan,
                          strategy='constant',
                          fill_value=-1))    
])

在这种情况下,测试 1 和测试 2 的转换失败,分别出现以下错误:

ValueError: Input contains NaN

ValueError: Input contains NaN, infinity or a value too large for dtype('float64').

我知道使用 pandas.DataFrame.replace 函数可以轻松完成这些类型的转换。但我对这种行为感到困惑,并感谢对每种情况下发生的事情的解释。

1. 第一个问题 - SimpleImputer 在满足以下条件时引发 ValueError (see documentation):

X.dtype.kind in ("f", "i", "u") and not isinstance(missing_values, numbers.Real)

供参考:isinstance(None, numbers.Real) returns Falseisinstance(np.nan, numbers.Real) returns True.

您管道中的输入器 'si1' 始终工作正常,因为 not isinstance(np.nan, numbers.Real) 始终是 False 并且整个条件是 False.

Imputer 'si2' 是错误的原因:not isinstance(None, numbers.Real)True 并且一切都取决于您传递给 SimpleImputer 的对象的数据类型。对于test1,它的dtype.kind是'o',但是对于test2,它的dtype.kind是'f'。因此,对于 test1,错误条件为 False,但对于 test2,错误条件为 True。

我不确定这种情况背后的意图是什么。可能的解决方法是单独使用 SimpleImputer 而不是将它们堆叠在管道中,并在将第一个 SimpleImputer 的结果传递给第二个之前更改 dtype。

2. 第二个问题 - 如果要传递给 SimpleImputer 的数组包含 np.nans,SimlpeImputer 需要 np.nan 作为 missing_value .否则会产生错误(see documentation).

我们只考虑test1,同样适用于test2。

a)考虑顺序:

pipeline = Pipeline([
('si1', SimpleImputer(missing_values = np.nan,
                      strategy='constant',
                      fill_value=-1)),
('si2', SimpleImputer(missing_values = None,
                      strategy='constant',
                      fill_value=-1))

])

当 test1 传递给 'si1' 时,它估算 np.nans,结果是

array([[0, -1],
       [False, -1],
       [0, -1]], dtype=object)

然后把这个数组传给'si2',结果就是

array([[0, -1],
       [False, -1],
       [0, -1]], dtype=object)

一切都很好。

b)现在考虑倒序:

pipeline = Pipeline([
('si2', SimpleImputer(missing_values = None,
                      strategy='constant',
                      fill_value=-1)),
('si1', SimpleImputer(missing_values = np.nan,
                      strategy='constant',
                      fill_value=-1))    

])

test1 包括 np.nans,但 'si2' 不归因 np.nans。正如预期的那样,这将产生 ValueError。