ColumnTransformer异常行为原因排查(Python/sklearn)
这个问题我之前也碰到过,其实是sklearn里两个组件的默认优化行为在共同作用,我来给你拆解清楚:
核心原因
1. ColumnTransformer的输出规则
ColumnTransformer在合并多个转换器的结果时,遵循一个简单的规则:只要有任何一个转换器返回稀疏矩阵,最终输出就会是稀疏矩阵;只有所有转换器都返回密集矩阵时,才会输出密集矩阵。
2. OneHotEncoder的自动稀疏切换
你用到的OneHotEncoder有个默认参数sparse_threshold(默认值为0.3),它的作用是:当独热编码后的矩阵密度(非零元素占总元素的比例)低于这个阈值时,自动切换为稀疏矩阵输出(节省内存);反之则返回密集矩阵。
对应到你的例子计算
我们来算一下两种情况的密度差异:
处理
samples[:5]时:
你选中的4个类别特征([0,1,3,4])独热编码后,总共有13个特征(特征0有5个唯一值+特征1只有1个+特征3有2个+特征4有5个)。总元素数是5*13=65,非零元素是5*4=20(每个样本的4个类别特征各对应1个1)。密度≈20/65≈0.307,刚好超过0.3的阈值,所以OneHotEncoder返回密集矩阵,ColumnTransformer最终输出也是密集矩阵。处理
samples[:6]时:
新增的第6个样本给特征0带来了新值1,给特征4带来了新值2720,独热后的总特征数变成15(6+1+2+6)。总元素数是6*15=90,非零元素还是6*4=24,密度≈24/90≈0.266,低于0.3的阈值,所以OneHotEncoder自动切换为稀疏输出,ColumnTransformer也就跟着返回稀疏矩阵了。
解决方法
如果希望始终得到密集矩阵,有两种简单的方式:
方式一:强制OneHotEncoder输出密集矩阵
在初始化OneHotEncoder时显式设置sparse_output=False(sklearn 1.0及以上版本用这个参数,旧版本用sparse=False):
categorical_transformer = Pipeline(steps=[ ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)) ])
方式二:手动转换稀疏矩阵为密集矩阵
如果你不想修改转换器的参数,可以在输出时调用toarray()方法:
print(scaler.fit_transform(samples[:6]).toarray())
补充说明
其实sklearn默认用稀疏矩阵是个优化行为——当类别特征多、维度高时,稀疏矩阵能大幅节省内存。但如果你的场景需要密集格式,用上面的方法就能轻松解决啦。
内容的提问来源于stack exchange,提问作者learningthemachine

