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

处理Kaggle太空泰坦尼克数据集时遇AttributeError: ndarray无columns属性

解决Scikit-learn Pipeline中出现的AttributeError: 'numpy.ndarray' object has no attribute 'columns'

问题描述

处理Kaggle Spaceship Titanic数据集时,使用自定义Scikit-learn Pipeline做数据预处理,触发了上述错误,报错发生在NullImputer的transform方法中——尝试访问X.columns但此时X已经变成了numpy数组,而非预期的Pandas DataFrame。

错误原因

  1. LabelYEncoder类设计错误:它继承了TransformerMixin,但实际作用是处理目标变量y,而非特征数据X。Pipeline中每个步骤的transform方法应接收并返回特征数据(通常是DataFrame),但LabelYEncoder.transform返回的是编码后的y数组,导致后续NullImputer接收到的是数组而非DataFrame,自然没有columns属性。
  2. Pipeline步骤顺序错误:目标变量的编码不应该嵌入特征预处理流程中,特征Pipeline仅负责处理X,y的编码需单独操作。

修复方案

  1. 移除Pipeline中的LabelYEncoder步骤,将y的编码从特征预处理流程中分离。
  2. 单独处理目标变量的编码。
  3. 配置Scikit-learn强制转换器返回Pandas DataFrame,避免自动转为numpy数组。

修复后完整代码

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import pandas as pd
from sklearn import set_config

# 强制Scikit-learn转换器返回Pandas DataFrame,避免自动转为numpy数组
set_config(transform_output="pandas")

class Cleaner(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        X = X.drop(["Name", "PassengerId", "FoodCourt", "ShoppingMall", "Cabin"], axis=1)
        return X

class ObjectEncoder(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        # 在fit阶段预拟合编码器,符合Scikit-learn API规范
        self.columns_to_encode = ["HomePlanet", "CryoSleep", "Destination", "VIP"]
        self.encoders = {}
        self.column_names = {}
        for column in self.columns_to_encode:
            encoder = OneHotEncoder(sparse_output=False, drop='first')
            encoder.fit(X[[column]])
            self.encoders[column] = encoder
            self.column_names[column] = [f"{column}_{cat}" for cat in encoder.categories_[0][1:]]
        return self
    
    def transform(self, X):
        X = X.copy()  # 避免修改原始数据
        for column in self.columns_to_encode:
            encoder = self.encoders[column]
            encoded_df = pd.DataFrame(
                encoder.transform(X[[column]]),
                columns=self.column_names[column],
                index=X.index
            )
            X = pd.concat([X, encoded_df], axis=1)
        
        # 删除原分类列
        X = X.drop(self.columns_to_encode, axis=1)
        return X

class NullImputer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        self.features_with_missing = X.columns[X.isnull().any()].tolist()
        self.imputer = SimpleImputer(strategy='median')
        self.imputer.fit(X[self.features_with_missing])
        return self
    
    def transform(self, X):
        X = X.copy()
        X[self.features_with_missing] = self.imputer.transform(X[self.features_with_missing])
        return X

# 构建特征预处理Pipeline
pipeline = Pipeline([
    ("cleaner", Cleaner()),
    ("object_encoder", ObjectEncoder()),
    ("null_imputer", NullImputer()),
])

# 分离特征与目标变量(替换为你的目标列名)
X_train = strat_train_set.drop("Transported", axis=1)
y_train = strat_train_set["Transported"]

# 处理特征数据
X_processed = pipeline.fit_transform(X_train)

# 单独处理目标变量编码
label_encoder = LabelEncoder()
y_processed = label_encoder.fit_transform(y_train)

关键修改说明

  • 给ObjectEncoder补充了fit阶段的编码器拟合逻辑,避免每次transform重复拟合,符合Scikit-learn的API设计规范。
  • 在transform方法中复制输入数据,避免修改原始数据集。
  • 使用set_config(transform_output="pandas")确保所有Scikit-learn转换器返回DataFrame,彻底杜绝自动转为numpy数组的问题。
  • 将目标变量的编码从Pipeline中分离,单独处理,明确了特征预处理与目标变量处理的边界。

内容的提问来源于stack exchange,提问作者PRADDYUMN YADAV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:54:56