Sklearn预处理管道报错ValueError:列'short_model'不存在,求排查
解决Data预处理管道中
short_model列不存在的报错 问题根源
ColumnTransformer的工作逻辑是并行独立处理每个transformer指定的列:每个transformer只拿到自己的目标列,处理后输出结果,最后将所有transformer的输出拼接成最终特征矩阵。你在ShortModelTransformer和AgeCategoryTransformer中生成的short_model、age_category列,仅存在于各自transformer的局部DataFrame中,不会被其他transformer(比如categorical)识别到,因此触发列不存在的报错。
另外,自定义transformer中直接修改原DataFrame的操作(如X['year'].loc[...])可能触发SettingWithCopyWarning,建议操作数据副本。
解决方案
把生成新列的特征工程步骤放到前置Pipeline中,先完成所有新特征的生成,再用ColumnTransformer对包含新列的完整DataFrame进行分类处理。
修改后的完整代码
import pandas as pd from sklearn.base import BaseEstimator, TransformerMixin from sklearn.impute import SimpleImputer from sklearn.preprocessing import OneHotEncoder, StandardScaler from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer class CalculateOutliers(BaseEstimator, TransformerMixin): def fit(self, X, y=None): return self def transform(self, X): # 操作副本避免修改原数据 X_copy = X.copy() q25 = X_copy['year'].quantile(0.25) q75 = X_copy['year'].quantile(0.75) iqr = q75 - q25 boundaries = (q25 - 1.5 * iqr, q75 + 1.5 * iqr) X_copy.loc[X_copy['year'] < boundaries[0], 'year'] = round(boundaries[0]) X_copy.loc[X_copy['year'] > boundaries[1], 'year'] = round(boundaries[1]) return X_copy class ShortModelTransformer(BaseEstimator, TransformerMixin): def fit(self, X, y=None): return self def short_model(self, x): if not pd.isna(x): return x.lower().split(' ')[0] else: return x def transform(self, X): X_copy = X.copy() X_copy['short_model'] = X_copy['model'].apply(self.short_model) return X_copy class AgeCategoryTransformer(BaseEstimator, TransformerMixin): def fit(self, X, y=None): return self def transform(self, X): X_copy = X.copy() X_copy['age_category'] = X_copy['year'].apply(lambda x: 'new' if x > 2013 else ('old' if x < 2006 else 'average')) return X_copy # 前置Pipeline:先完成所有新特征生成 feature_engineering = Pipeline(steps=[ ('outliers', CalculateOutliers()), ('short_model', ShortModelTransformer()), ('age_category', AgeCategoryTransformer()) ]) # 分类特征处理管道 categorical_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='most_frequent')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False)) ]) # 数值特征处理管道 numerical_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='median')), ('encoder', StandardScaler()) ]) # 完整预处理管道:先生成特征,再分类处理 preprocessor = Pipeline(steps=[ ('feature_gen', feature_engineering), ('preprocess', ColumnTransformer(transformers=[ ('numerical', numerical_transformer, ['year', 'odometer']), ('categorical', categorical_transformer, ['fuel', 'title_status', 'transmission', 'state', 'short_model', 'age_category']) ])) ])
关键说明
- 前置Pipeline
feature_engineering会依次执行所有特征生成步骤,每一步都基于上一步的完整DataFrame,因此short_model和age_category会被添加到全局DataFrame中。 - 后续的ColumnTransformer拿到的是经过特征工程后的完整DataFrame,可以直接引用新生成的列。
- 所有自定义transformer都操作DataFrame的副本,避免了原数据被修改和潜在的警告。
内容的提问来源于stack exchange,提问作者Aleksandr Pay
相关产品推荐
相关产品推荐

