You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Transformer集成多转换器报错:ValueError: bad input shape

自定义Transformer在Pipeline中报错的问题解析与优化

问题描述

我实现了一个用于对目标DataFrame进行编码和缩放的自定义Transformer,代码如下:

class DfGrooming(BaseEstimator, TransformerMixin):
    def __init__(self):
        self.encodable_columns = ['Education','EmploymentType','MaritalStatus', 'HasMortgage', 'HasDependents', 'LoanPurpose', 'HasCoSigner']
        self.scalable_columns = ['Age', 'Income', 'LoanAmount', 'CreditScore', 'MonthsEmployed', 'InterestRate', 'LoanTerm']
        self.encoder = LabelEncoder()
        self.scaler = MinMaxScaler(feature_range=(0,5))
        self.X_encoded = pd.DataFrame()
        self.X_scaled = pd.DataFrame()
    
def fit(self, X, y=None):
    self.encoder.fit(X[self.encodable_columns])
    self.scaler.fit(X[self.scalable_columns])
    return self

def transform(self, X, y=None):
    self.X_encoded = self.encoder.transform(X[self.encodable_columns])
    print(self.X_encoded.shape)
    X.drop(columns=self.encodable_columns, axis=1, inplace=True)
    X = pd.concat([X, self.X_encoded], axis=1)
    print(X.shape)
    self.X_scaled = X.filter(self.scalable_columns, axis=1)
    self.X_scaled = pd.DataFrame(scaler.transform(self.X_scaled))
    self.X_scaled.columns = self.scalable_columns
    X[self.scalable_columns] = self.X_scaled[self.scalable_columns]
    X.drop(['LoanID'], axis=1, inplace=True)
    print(X.shape)
    
    return X

运行如下Pipeline时:

pipeline = Pipeline([('preparer', DfGrooming())])
t = pipeline.fit_transform(train_df)
t.head()

出现错误:

ValueError: bad input shape (178742, 7)

希望了解该错误的原因、当前实现的疏漏,并请求更优的实现方案。此前通过半自动化函数处理验证集和测试集,现在尝试改用Pipeline完成预处理。

错误原因

  • LabelEncoder不支持多列输入:LabelEncoder是为单个特征(一维数组)设计的,你传入了7列数据(encodable_columns共7个),fit和transform时都会因输入维度不匹配报错,这就是bad input shape (178742, 7)的直接原因。

实现中的疏漏

  1. 错误使用LabelEncoder处理多列:该工具仅适用于单特征标签编码,多列分类特征应使用OrdinalEncoder或OneHotEncoder。
  2. 直接修改输入DataFrame:transform中使用X.drop(..., inplace=True)会修改原始输入数据,破坏Pipeline无副作用的设计原则,可能引发后续流程异常。
  3. 未引用类实例的scaler对象:代码中写scaler.transform而非self.scaler.transform,会触发未定义变量错误。
  4. 编码后数据拼接逻辑错误:即便LabelEncoder能处理多列,返回的结果维度也无法直接与原DataFrame对齐拼接。
  5. 硬编码列名:drop(['LoanID'])属于固定操作,若后续数据结构变化会直接报错,缺乏灵活性。

优化后的实现方案

改用OrdinalEncoder处理多列分类特征,同时遵循Transformer最佳实践,保证代码鲁棒性:

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import OrdinalEncoder, MinMaxScaler
import pandas as pd

class DfGrooming(BaseEstimator, TransformerMixin):
    def __init__(self, encodable_columns=None, scalable_columns=None, drop_columns=['LoanID']):
        # 允许外部传入列名,增强适配性
        self.encodable_columns = encodable_columns or ['Education','EmploymentType','MaritalStatus', 'HasMortgage', 'HasDependents', 'LoanPurpose', 'HasCoSigner']
        self.scalable_columns = scalable_columns or ['Age', 'Income', 'LoanAmount', 'CreditScore', 'MonthsEmployed', 'InterestRate', 'LoanTerm']
        self.drop_columns = drop_columns
        # 配置未知类别处理,避免测试集出现新类别报错
        self.encoder = OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)
        self.scaler = MinMaxScaler(feature_range=(0,5))
    
    def fit(self, X, y=None):
        # 仅对目标列执行拟合
        self.encoder.fit(X[self.encodable_columns])
        self.scaler.fit(X[self.scalable_columns])
        return self
    
    def transform(self, X, y=None):
        # 复制输入数据,避免修改原始DataFrame
        X_transformed = X.copy()
        
        # 处理分类列编码,保留原索引确保拼接对齐
        encoded_cols = pd.DataFrame(
            self.encoder.transform(X_transformed[self.encodable_columns]),
            columns=self.encodable_columns,
            index=X_transformed.index
        )
        X_transformed = X_transformed.drop(columns=self.encodable_columns)
        X_transformed = pd.concat([X_transformed, encoded_cols], axis=1)
        
        # 处理数值列缩放
        scaled_cols = pd.DataFrame(
            self.scaler.transform(X_transformed[self.scalable_columns]),
            columns=self.scalable_columns,
            index=X_transformed.index
        )
        X_transformed[self.scalable_columns] = scaled_cols
        
        # 移除指定列,忽略不存在的列避免报错
        if self.drop_columns:
            X_transformed = X_transformed.drop(columns=self.drop_columns, errors='ignore')
        
        return X_transformed

优化点说明

  • 替换LabelEncoder为OrdinalEncoder:原生支持多列输入,还能处理测试集中的未知类别。
  • 避免修改原始数据:所有操作基于X.copy(),保证输入数据独立性。
  • 增强灵活性:列名通过参数传入,适配不同数据集。
  • 规范数据拼接:编码/缩放后的数据保留原索引,确保与原DataFrame对齐。
  • 容错处理:移除列时使用errors='ignore',避免因列不存在报错。

Pipeline使用验证

from sklearn.pipeline import Pipeline

pipeline = Pipeline([('preparer', DfGrooming())])
# 训练集拟合+转换
t = pipeline.fit_transform(train_df)
print(t.head())

# 测试集直接转换,自动复用训练集拟合的参数
test_processed = pipeline.transform(test_df)

内容的提问来源于stack exchange,提问作者Aditya Shandilya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:22:06