使用sklearn ColumnTransformer时特征名长度与X实际维度不匹配
sklearn预处理流水线特征名数量与转换后维度不匹配问题
问题场景
搭建如下预处理流水线用于模型训练:
from sklearn.compose import make_column_selector as selector from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder, StandardScaler from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer cat_imputer = SimpleImputer(strategy='constant',fill_value='missing') num_imputer = SimpleImputer(strategy='constant',fill_value=0,add_indicator=True) categorical_pipeline = Pipeline([ ('imputer',cat_imputer), ('encoder',OneHotEncoder()) ]) numerical_pipeline = Pipeline([ ('imputer',num_imputer) ]) def get_column_types(X): numerical_columns = numerical_columns_selector(X) categorical_columns = categorical_columns_selector(X) return numerical_columns, categorical_columns def get_transformer(X,y): numerical_columns, categorical_columns = get_column_types(X) pre_transformer = ColumnTransformer([ ('cat_pipe', pre_categorical_pipeline, categorical_columns), ('num_pipe', pre_numerical_pipeline, numerical_columns) ]) return transformer
拟合转换器后,使用如下代码提取特征名,出现特征数量不匹配问题:
transformer = models_and_pipelines.get_transformer(X,y) X = transformer.fit_transform(X) # 提取特征名,尝试其他同类特征提取函数得到相同结果 starting_features = list(transformer.transformers_[0][1]['encoder'].get_feature_names()) + list(transformer.transformers_[1][2]) print(X.shape[1]) print(len(starting_features))
运行输出:
1094 1090
转换后的特征矩阵维度为1094,但手动拼接的特征名仅1090个,二者差4个特征。
根本原因
- 差值来自数值特征预处理器的配置:初始化数值列缺失值填充器
SimpleImputer时,设置了add_indicator=True。该参数开启后,会对每一列存在缺失值的数值特征,额外生成1列0/1指示特征,标记该行对应位置是否为填充的缺失值。手动拼接特征名时,仅统计了原始数值列的列名,没有计入这部分额外生成的缺失指示特征,差的4个特征正好对应4列存在缺失值的数值列生成的指示列。 - 现有特征拼接逻辑还存在两处隐患:
- 调用
OneHotEncoder.get_feature_names()时没有传入原始分类列名,生成的特征名默认是x0_xxx、x1_xxx这类无业务含义的名称,无法映射到原始字段 - 代码存在变量名笔误:
get_transformer函数内定义的预处理器名为pre_transformer,分类、数值流水线变量名为categorical_pipeline/numerical_pipeline,但函数内引用的是未定义的pre_categorical_pipeline/pre_numerical_pipeline,最终返回的也是未定义的transformer变量,运行时会直接抛出变量不存在的错误。
- 调用
修复方法
手动拼接特征名时,需要把缺失值指示器生成的特征名也纳入统计,参考实现:
# 提取分类流水线独热编码生成的特征名 cat_pipe = transformer.transformers_[0][1] cat_features = list(cat_pipe['encoder'].get_feature_names_out(cat_pipe.feature_names_in_)) # 提取数值流水线原始列名 + 缺失指示器生成的特征名 num_pipe = transformer.transformers_[1][1] num_features = list(num_pipe.feature_names_in_) if hasattr(num_pipe['imputer'], 'indicator_'): num_features += list(num_pipe['imputer'].indicator_.get_feature_names_out()) all_features = cat_features + num_features
提示:sklearn 1.0及以上版本中,所有转换器都自带
get_feature_names_out方法,拟合完成后直接调用transformer.get_feature_names_out()即可拿到全量正确的特征名,无需手动拼接。
内容的提问来源于stack exchange,提问作者Jeroen Vermunt
相关产品推荐
相关产品推荐

