管道内部的列转换问题

时间:2019-05-18 08:50:29

标签: python pandas scikit-learn

我正在尝试建立包含几个用户定义列的管道 转变。当创建一个新的列转换器时,我继承了 sklearn.base.BaseEstimatorsklearn.base.TransformerMixin,以及 实现fittransform方法。调用转换 直接按预期工作,但将其用作 sklearn.pipeline.Pipeline实例无法给出模棱两可的错误。

假设我有一个pandas.DataFrame实例df,其中包含以下数据:

       date   genre
0   9/22/11  horror
1   1/16/04    NULL
2  10/11/96    NULL
3   3/28/13   drama
4   4/22/94   drama

我想实现两个转换器:

  1. DateTransformer,用于转换df['date']中的日期字符串 放入一个numpy.array实例,其中包含每一行的年,月和日。

  2. GenreTransformer,对于df['genre']中的每个流派,如果 未指定(“ NULL”),否则未指定-1。

这是我的代码:

class GenreTransformer(BaseEstimator, TransformerMixin):
    def fit(self, x, y=None):
        return self

    def transform(self, x):
        x_copy = x.copy()
        x_copy[x_copy != 'NULL'] = -1
        x_copy[x_copy == 'NULL'] = 1
        return x_copy.values

class DateTransformer(BaseEstimator, TransformerMixin):
    def fit(self, x, y=None):
        return self

    def transform(self, x):
        x_timestamp = x.apply(pd.to_datetime)
        return np.column_stack((
            x_timestamp.apply(lambda t: t.year).values,
            x_timestamp.apply(lambda t: t.month).values,
            x_timestamp.apply(lambda t: t.day).values,
        ))

两个变压器都能正常工作:

>>> GenreTransformer().fit_transform(df['genre'])
array([-1, 1, 1, -1, -1])

>>> DateTransformer().fit_transform(df['date'])
array([[2011,    9,   22],
       [2004,    1,   16],
       [1996,   10,   11],
       [2013,    3,   28],
       [1994,    4,   22]])

但是,当我使用合并变压器时 sklearn.compose.ColumnTransformer,并创建管道, DateTransformer不起作用:

column_transformer = ColumnTransformer(
    transformers=[
        ('date_trans', DateTransformer(), ['date']),
        ('genre_trans', GenreTransformer(), ['genre']),
    ],
    remainder='drop',
)

pipe = Pipeline(
    steps=[
        ('union', column_transformer),
        # estimators
    ],
)
>>> pipe.fit(df)
---------------------------------------------------------------------------
Traceback (most recent call last)
...
AttributeError: ("'Series' object has no attribute 'year'", 'occurred at index date')

有趣的是,改用pandas.Series.apply GenreTransformer.transform内的遮罩方法集以及管件的装配 失败:

class GenreTransformer(BaseEstimator, TransformerMixin):
    # ...
    def transform(self, x):
        return x.apply(lambda g: -1 if g != 'NULL' else 1)
>>> pipe.fit(df)
---------------------------------------------------------------------------
Traceback (most recent call last)
...
ValueError: ('The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().', 'occurred at index genre')

因此,我认为应用pandas.Series.apply方法存在问题 内部管道。 scikit-learn源代码是否可能存在错误? 还是我做错了什么?如果是这样,请您指出 如何实现列转换器,以便可以将它们包含在管道中?

1 个答案:

答案 0 :(得分:1)

您的代码中有一个细微的错误。

您为要应用['date']的列指定了DateTransformer。这样做时,[它表示DateTransformer期望类似于2D数组],在这种情况下,它是DataFrame。但是,实际上它期望像一维数组或Series

因此,当您实际需要DateTransformer().fit_transform(df[['date']])时,您所做的等同于df['date']

相应地,将('date_trans', DateTransformer(), 'date')传递给ColumnTransformer,一切都会好起来。