用于改变X和y的sklearn管道的自定义变换器

时间:2014-08-28 01:20:08

标签: python numpy machine-learning scikit-learn data-analysis

我想创建自己的变换器以与sklearn Pipeline一起使用。因此,我正在创建一个实现fit和transform方法的类。变换器的目的是从矩阵中删除具有超过指定数量的NaN的行。所以我面临的问题是如何更改传递给变压器的X和y矩阵?我相信这必须在fit方法中完成,因为它可以同时访问X和y。因为一旦我将X重新分配给具有较少行的新矩阵,python就会通过赋值传递参数,因此对原始X的引用将丢失(当然对于y也是如此)。是否可以保留此参考?

我正在使用pandas DataFrame轻松删除具有太多NaN的行,这可能不适合我的用例。当前代码如下所示:

class Dropna():

    # thresh is max number of NaNs allowed in a row
    def __init__(self, thresh=0):
        self.thresh = thresh

    def fit(self, X, y):
        total = X.shape[1]
        # +1 to account for 'y' being added to the dframe                                                                                                                            
        new_thresh = total + 1 - self.thresh
        df = pd.DataFrame(X)
        df['y'] = y
        df.dropna(thresh=new_thresh, inplace=True)
        X = df.drop('y', axis=1).values
        y = df['y'].values
        return self

    def transform(self, X):
        return X

4 个答案:

答案 0 :(得分:7)

修改样本轴,例如删除样本,(但是?)是否符合scikit-learn变换器API。因此,如果您需要这样做,您应该在任何调用scikit学习之外的时候进行预处理。

现在,变换器API用于将给定样本的特征转换为新的特征。这可以隐含地包含来自其他样本的信息,但样本永远不会被删除。

另一个选择是尝试估算缺失的值。但同样,如果您需要删除样本,请在使用scikit learn之前将其视为预处理。

答案 1 :(得分:0)

您可以使用sklearn.preprocessing.FunctionTransformer方法(http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.FunctionTransformer.html)轻松解决此问题

您只需要在功能

中将替换放到X.
def drop_nans(X, y=None):
    total = X.shape[1]                                           
    new_thresh = total - thresh
    df = pd.DataFrame(X)
    df.dropna(thresh=new_thresh, inplace=True)
    return df.values

然后通过调用

获得变压器
transformer = FunctionTransformer(drop_nans, validate=False)

您可以在管道中使用。可以在drop_nans函数之外设置阈值。

答案 2 :(得分:0)

@eickenberg 是正确而干净的答案。尽管如此,我喜欢把所有东西都放在一个流水线中,所以如果你有兴趣,我创建了一个库(尚未部署在 pypi 上)允许在 Y 上应用转换:

https://gitlab.com/thibaultB/transformers/

用法如下:

df = pd.DataFrame([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
df.columns = ["a", "b", "target"]

spliter = SplitXY("target") # Create a new step and give it name of column target

pipe = Pipeline([
        ("imputer", SklearnPandasWrapper(KNNImputer())),
        ("spliter", spliter), 
        ("scaler", StandardScaler()),
        ("rf",
            EstimatorWithoutYWrapper(RandomForestRegressor(random_state=45),
                                    spliter)) # EstimatorWithoutYWrapper overwrite RandomForestRegressor to get y from spliter just before calling fit or transform
    ])
pipe.fit(df)

res = pipe.predict(df)

使用此代码,如果将所有修改行数的转换器放在“SplitXY”转换器之前,则可以更改行数。在 SplitXY 转换器之前的转换器应该保留列名,这就是为什么我还添加了一个 SklearnPandasWrapper 来包装 sklearn 转换器(通常返回 numpy 数组)以保留列名。

答案 3 :(得分:-1)

在管道下方进一步使用“深层副本”, X y 仍然受到保护

.fit() 可以先为每次调用深层复制分配新的类变量

self.X_without_NaNs = X.copy()
self.y_without_NaNs = y.copy()

然后缩小/转换它们,使NaN - s比self.treshold

排序更多