您可以使用Sklearn的Transformer API一致地跟踪列标签吗?

Jon*_*tel 5 python pandas scikit-learn

对于这个库来说,这似乎是一个非常重要的问题,到目前为止,我没有一个决定性的答案,尽管在大多数情况下,答案是“不”。

现在,任何使用transformerapi的方法都会sklearn返回一个numpy数组作为其结果。通常这很好,但是如果您将一个扩展或减少列数的多步骤过程链接在一起,则没有一种干净的方法来跟踪它们与原始列标签的关系将使使用此部分内容变得困难。充分发挥图书馆的作用。

例如,这是我最近使用的一个片段,其中无法将新列映射到数据集中的原始列是一个很大的缺点:

numeric_columns = train.select_dtypes(include=np.number).columns.tolist()
cat_columns     = train.select_dtypes(include=np.object).columns.tolist()

numeric_pipeline = make_pipeline(SimpleImputer(strategy='median'), StandardScaler())
cat_pipeline     = make_pipeline(SimpleImputer(strategy='most_frequent'), OneHotEncoder())

transformers = [
('num', numeric_pipeline, numeric_columns),
('cat', cat_pipeline, cat_columns)
]

combined_pipe = ColumnTransformer(transformers)

train_clean = combined_pipe.fit_transform(train)

test_clean  = combined_pipe.transform(test)
Run Code Online (Sandbox Code Playgroud)

在此示例中,我使用拆分了数据集ColumnTransformer,然后使用了OneHotEncoder,添加了其他列,因此列的排列方式与我开始时的排列方式不同。

如果我使用使用相同API的不同模块,则可以轻松地进行不同的安排。 OrdinalEncoerselect_k_best等等。

如果您要进行多步转换,是否可以始终如一地查看新列与原始数据集之间的关系?

有一个关于它的广泛讨论这里,但我不认为有什么已经没有最终确定。

Ven*_*lam 34

是的,您是对的,目前还没有完全支持跟踪 feature_names sklearn。最初,决定在numpy数组级别保持它的通用性。可以在此处跟踪将特征名称添加到 sklearn 估计器的最新进展。

无论如何,我们可以创建包装器来获取ColumnTransformer. 我不确定它是否可以捕获所有可能类型的ColumnTransformers. 但至少,它可以解决你的问题。

来自Documentation of ColumnTransformer

笔记

转换后的特征矩阵中列的顺序遵循在转换器列表中指定列的顺序。除非在 passthrough 关键字中指定,否则未指定的原始特征矩阵的列将从生成的转换特征矩阵中删除。那些通过直通指定的列被添加到变压器输出的右侧。

尝试这个!

import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import make_pipeline, Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder, MinMaxScaler
from sklearn.feature_extraction.text import _VectorizerMixin
from sklearn.feature_selection._base import SelectorMixin
from sklearn.feature_selection import SelectKBest
from sklearn.feature_extraction.text import CountVectorizer

train = pd.DataFrame({'age': [23,12, 12, np.nan],
                      'Gender': ['M','F', np.nan, 'F'],
                      'income': ['high','low','low','medium'],
                      'sales': [10000, 100020, 110000, 100],
                      'foo' : [1,0,0,1],
                      'text': ['I will test this',
                               'need to write more sentence',
                               'want to keep it simple',
                               'hope you got that these sentences are junk'],
                      'y': [0,1,1,1]})
numeric_columns = ['age']
cat_columns     = ['Gender','income']

numeric_pipeline = make_pipeline(SimpleImputer(strategy='median'), StandardScaler())
cat_pipeline     = make_pipeline(SimpleImputer(strategy='most_frequent'), OneHotEncoder())
text_pipeline = make_pipeline(CountVectorizer(), SelectKBest(k=5))

transformers = [
('num', numeric_pipeline, numeric_columns),
('cat', cat_pipeline, cat_columns),
('text', text_pipeline, 'text'),
('simple_transformer', MinMaxScaler(), ['sales']),
]

combined_pipe = ColumnTransformer(transformers, remainder='passthrough')

transformed_data = combined_pipe.fit_transform(train.drop('y',1), train['y'])

Run Code Online (Sandbox Code Playgroud)
def get_feature_out(estimator, feature_in):
    if hasattr(estimator,'get_feature_names'):
        if isinstance(estimator, _VectorizerMixin):
            # handling all vectorizers
            return [f'vec_{f}' \
                for f in estimator.get_feature_names()]
        else:
            return estimator.get_feature_names(feature_in)
    elif isinstance(estimator, SelectorMixin):
        return np.array(feature_in)[estimator.get_support()]
    else:
        return feature_in


def get_ct_feature_names(ct):
    # handles all estimators, pipelines inside ColumnTransfomer
    # doesn't work when remainder =='passthrough'
    # which requires the input column names.
    output_features = []

    for name, estimator, features in ct.transformers_:
        if name!='remainder':
            if isinstance(estimator, Pipeline):
                current_features = features
                for step in estimator:
                    current_features = get_feature_out(step, current_features)
                features_out = current_features
            else:
                features_out = get_feature_out(estimator, features)
            output_features.extend(features_out)
        elif estimator=='passthrough':
            output_features.extend(ct._feature_names_in[features])
                
    return output_features

pd.DataFrame(transformed_data, 
             columns=get_ct_feature_names(combined_pipe))
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 我认为重要的是要注意,如果您定义自定义估计器,以及使用此方法的用途,则该估计器必须实现“get_feature_names”方法 (3认同)
  • 如果新函数不命名为“get_feature_names”会更清楚,因为它与 sklearn 管道函数的命名相同。(参见“i.get_feature_names”) (2认同)