【问题标题】:Preserve column order after applying sklearn.compose.ColumnTransformer应用 sklearn.compose.ColumnTransformer 后保留列顺序
【发布时间】:2021-10-22 17:38:37
【问题描述】:

我正在使用 sklearn 库中的 PipelineColumnTransformer 模块对我的数据集执行特征工程。

数据集最初如下所示:

date date_block_num shop_id item_id item_price
02.01.2013 0 59 22154 999.00
03.01.2013 0 25 2552 899.00
05.01.2013 0 25 2552 899.00
06.01.2013 0 25 2554 1709.05
15.01.2013 0 25 2555 1099.00
$> data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2935849 entries, 0 to 2935848
Data columns (total 6 columns):
 #   Column          Dtype  
---  ------          -----  
 0   date            object 
 1   date_block_num  object  
 2   shop_id         object  
 3   item_id         object  
 4   item_price      float64
dtypes: float64(2), int64(3), object(1)
memory usage: 134.4+ MB

然后我有以下变换:

num_column_transformer = ColumnTransformer(
    transformers=[
        ("std_scaler", StandardScaler(), make_column_selector(dtype_include=np.number)),
    ],
    remainder="passthrough"
)

num_pipeline = Pipeline(
    steps=[
        ("percent_item_cnt_day_per_shop", PercentOverTotalAttributeWholeAdder(
            attribute_percent_name="shop_id",
            attribute_total_name="item_cnt_day",
            new_attribute_name="%_item_cnt_day_per_shop")
        ),
        ("percent_item_cnt_day_per_item", PercentOverTotalAttributeWholeAdder(
            attribute_percent_name="item_id",
            attribute_total_name="item_cnt_day",
            new_attribute_name="%_item_cnt_day_per_item")
        ),
        ("percent_sales_per_shop", SalesPerAttributeOverTotalSalesAdder(
            attribute_percent_name="shop_id",
            new_attribute_name="%_sales_per_shop")
        ),
        ("percent_sales_per_item", SalesPerAttributeOverTotalSalesAdder(
            attribute_percent_name="item_id",
            new_attribute_name="%_sales_per_item")
        ),
        ("num_column_transformer", num_column_transformer),
    ]
)

前四个Transformers 创建四个新的不同数值变量,最后一个将StandardScaler 应用于数据集的所有数值。

执行后得到如下数据:

0 1 2 3 4 5 6 7 8
-0.092652 -0.765612 -0.173122 -0.756606 -0.379775 02.01.2013 0 59 22154
-0.092652 1.557684 -0.175922 1.563224 -0.394319 03.01.2013 0 25 2552
-0.856351 1.557684 -0.175922 1.563224 -0.394319 05.01.2013 0 25 2552
-0.092652 1.557684 -0.17613 1.563224 -0.396646 06.01.2013 0 25 2554
-0.092652 1.557684 -0.173278 1.563224 -0.380647 15.01.2013 0 25 2555

我想要以下输出:

date date_block_num shop_id item_id item_price %_item_cnt_day_per_shop %_item_cnt_day_per_item %_sales_per_shop %_sales_per_item
02.01.2013 0 59 22154 -0.092652 -0.765612 -0.173122 -0.756606 -0.379775
03.01.2013 0 25 2552 -0.092652 1.557684 -0.175922 1.563224 -0.394319
05.01.2013 0 25 2552 -0.856351 1.557684 -0.175922 1.563224 -0.394319
06.01.2013 0 25 2554 -0.092652 1.557684 -0.17613 1.563224 -0.396646
15.01.2013 0 25 2555 -0.092652 1.557684 -0.173278 1.563224 -0.380647

如您所见,输出中的列5678 对应于原始数据集中的前四列。例如,我不知道item_price 特征在输出表中的位置。

  1. 如何保留列顺序和名称?之后,我想对分类变量进行特征工程,而我的 Transformer 使用特征列名称。
  2. 我是否正确使用了 Scikit-Learn API?

【问题讨论】:

    标签: python pandas scikit-learn data-science


    【解决方案1】:

    ColumnTransformer打交道有一点需要注意,doc内报告如下:

    变换后的特征矩阵中列的顺序遵循变换器列表中列的指定顺序

    这就是你的ColumnTransformer 实例搞砸的原因。事实上,考虑这个类似于您的设置的简化示例:

    import numpy as np
    import pandas as pd
    from sklearn.compose import ColumnTransformer, make_column_selector
    from sklearn.preprocessing import StandardScaler
    from sklearn.pipeline import Pipeline
    
    df = pd.DataFrame({
                   'date': ['02.01.2013', '03.01.2013', '05.01.2013', '06.01.2013', '15.01.2013'], 
                   'date_block_num': ['0', '0', '0', '0', '0'], 
                   'shop_id': ['59', '25', '25', '25', '25'],
                   'item_id': ['22514', '2252', '2252', '2254', '2255'], 
                   'item_price': [999.00, 899.00, 899.00, 1709.05, 1099.00]})
    
    ct = ColumnTransformer([
        ('std_scaler', StandardScaler(), make_column_selector(dtype_include=np.number))], 
        remainder='passthrough')
    
    pd.DataFrame(ct.fit_transform(df), columns=ct.get_feature_names_out())
    

    您可能会注意到,转换后的数据框中的第一列原来是 numeric 的,即经过缩放的那一列(以及 转换器列表中的第一列 em>)。

    相反,这是一个示例,说明如何通过在传递所有字符串变量后推迟对数值变量的缩放,从而确保以所需顺序获取列的可能性,从而绕过此类问题:

    ct = ColumnTransformer([
        ('pass', 'passthrough', make_column_selector(dtype_include=object)),
        ('std_scaler', StandardScaler(), make_column_selector(dtype_include=np.number))
    ])
    
    pd.DataFrame(ct.fit_transform(df), columns=ct.get_feature_names_out())
    

    为了完成图片,这里尝试重现您的管道(尽管自定义转换器肯定与您的略有不同):

    from sklearn.base import BaseEstimator, TransformerMixin
    
    class PercentOverTotalAttributeWholeAdder(BaseEstimator, TransformerMixin):
    
        def __init__(self, attribute_percent_name='shop_id', new_attribute_name='%_item_cnt_day_per_shop'):
        self.attribute_percent_name = attribute_percent_name
        self.new_attribute_name = new_attribute_name
        
        def fit(self, X, y=None):
            return self
    
        def transform(self, X, y=None):
            df[self.new_attribute_name] = df.groupby(by=self.attribute_percent_name)[self.attribute_percent_name].transform('count') / df.shape[0]
            return df
    
    ct_pipe = ColumnTransformer([
        ('pass', 'passthrough', make_column_selector(dtype_include=object)),
        ('std_scaler', StandardScaler(), make_column_selector(dtype_include=np.number))
        ], verbose_feature_names_out=False)
    
    pipe = Pipeline([
        ('percent_item_cnt_day_per_shop', PercentOverTotalAttributeWholeAdder(
            attribute_percent_name='shop_id',
            new_attribute_name='%_item_cnt_day_per_shop')
        ),
        ('percent_item_cnt_day_per_item', PercentOverTotalAttributeWholeAdder(
            attribute_percent_name='item_id',
            new_attribute_name='%_item_cnt_day_per_item')
        ),
        ('column_trans', ct_pipe),
    ])
    
    pd.DataFrame(pipe.fit_transform(df), columns=pipe[-1].get_feature_names_out())
    

    最后,请注意verbose_feature_names_out=False 参数确保转换后的数据帧的列名称不显示引用ColumnTransformer 中不同转换器的前缀。

    【讨论】:

      猜你喜欢
      • 2022-11-12
      • 2012-08-13
      • 1970-01-01
      • 1970-01-01
      • 2015-12-08
      • 1970-01-01
      • 1970-01-01
      • 2022-08-08
      • 2015-08-26
      相关资源
      最近更新 更多