使用ColumnTransformer进入管道时出现AttributeError
问题内容:
这是我的第一个机器学习项目,也是我第一次使用ColumnTransformer。我的目标是执行数据预处理的两个步骤,并对每个步骤使用ColumnTransformer。
第一步,我想将数据框中的缺失值替换为某些功能的字符串’missing_value’,并使用其余功能的最常用值替换。因此,我使用ColumnTransformer结合了这两个操作,并将数据框的相应列传递给它。
在第二步中,我要使用仅预处理的数据,并根据功能应用OrdinalEncoder或OneHotEncoder。为此,我再次使用ColumnTransformer。
然后,我将两个步骤合并为一个管道。
我正在使用Kaggle房屋价格数据集,我的scikit-learn版本为0.20,这是我代码的简化版本:
cat_columns_fill_miss = ['PoolQC', 'Alley']
cat_columns_fill_freq = ['Street', 'MSZoning', 'LandContour']
cat_columns_ord = ['Street', 'Alley', 'PoolQC']
ord_mapping = [['Pave', 'Grvl'], # Street
['missing_value', 'Pave', 'Grvl'], # Alley
['missing_value', 'Fa', 'TA', 'Gd', 'Ex'] # PoolQC
]
cat_columns_onehot = ['MSZoning', 'LandContour']
imputer_cat_pipeline = ColumnTransformer([
('imp_miss', SimpleImputer(strategy='constant'), cat_columns_fill_miss), # fill_value='missing_value' by default
('imp_freq', SimpleImputer(strategy='most_frequent'), cat_columns_fill_freq),
])
encoder_cat_pipeline = ColumnTransformer([
('ordinal', OrdinalEncoder(categories=ord_mapping), cat_columns_ord),
('pass_ord', OneHotEncoder(), cat_columns_onehot),
])
cat_pipeline = Pipeline([
('imp_cat', imputer_cat_pipeline),
('cat_encoder', encoder_cat_pipeline),
])
不幸的是,当我将其应用于housing_cat时,我的数据框的子集仅包含分类特征,
cat_pipeline.fit_transform(housing_cat)
我得到错误:
AttributeError:“ numpy.ndarray”对象没有属性“ columns”
在处理上述异常期间,发生了另一个异常:
…
ValueError:仅对pandas DataFrames支持使用字符串指定列
我已经尝试过这个简化的管道,并且可以正常工作:
new_cat_pipeline = Pipeline([
('imp_cat', imputer_cat_pipeline),
('onehot', OneHotEncoder()),
])
但是,如果我尝试:
enc_one = ColumnTransformer([
('onehot', OneHotEncoder(), cat_columns_onehot),
('pass_ord', 'passthrough', cat_columns_ord)
])
new_cat_pipeline = Pipeline([
('imp_cat', imputer_cat_pipeline),
('onehot_encoder', enc_one),
])
我开始遇到同样的错误。
然后,我怀疑此错误与第二步中使用ColumnTransformer有关,但我实际上并不了解它来自何处。我在第二步中识别列的方式与第一步中相同,因此我仍然不清楚为什么只有在第二步中才出现属性错误…
问题答案:
ColumnTransformer
返回numpy.array
,因此它不能具有列属性(如您的错误所示)。
如果我建议使用其他解决方案,将其pandas
用于您的两个任务,将会更容易。
第1步-替换缺失值
要用missing_value
字符串替换列子集中的缺失值,请使用以下命令:
dataframe[["PoolQC", "Alley"]].fillna("missing_value", inplace=True)
对于其余的(用每列的平均值进行插补),这将完美地工作:
dataframe[["Street", "MSZoning", "LandContour"]].fillna(
dataframe[["Street", "MSZoning", "LandContour"]].mean(), inplace=True
)
第2步-一种热编码和分类变量
pandas
提供get_dummies
,它返回的是熊猫Dataframe,而不同于ColumnTransfomer
,代码如下:
encoded = pd.get_dummies(dataframe[['MSZoning', 'LandContour']], drop_first=True)
pd.dropna(['MSZoning', 'LandContour'], axis=columns, inplace=True)
dataframe = dataframe.join(encoded)
对于序数变量及其编码,我建议您看一下这个SO答案(不幸的是,在这种情况下,需要一些手动映射)。
如果仍然要使用变压器
np.array
使用values
属性从数据框获取,通过管道传递它,然后从数组中重新创建列和索引,如下所示:
pd.DataFrame(data=your_array, index=np.arange(len(your_array)), columns=["A", "B"])
但是,这种方法有一个警告:您将不知道自定义创建的一键编码列的名称(管道不会为您完成此操作)。
另外,您可以从sklearn的转换对象中获取列的名称(例如,使用categories_
属性),但我认为这会中断管道(如果我错了,请纠正我)。