如何在Pandas中转换多分类名义型类别列(规避维度爆炸)
嘿,这个问题太典型了——高基数的名义型特征编码确实头疼:Item_Identifier这类无顺序的名义特征,用LabelEncoder完全不符合逻辑(毕竟类别间没大小先后),搞哑变量又直接生成1599列,和你的训练集行数一样,直接维度爆炸还可能导致模型无法训练。下面给你几个靠谱的解决方案,按需选择:
1. 频率/计数编码(无监督场景首选)
既然你已经有了value_counts()的结果,大部分类别出现次数在9-10次,我们可以把每个类别映射成它在数据集里的出现频率(或计数)。这种方法既保留了类别分布的信息,又不会新增任何列,简单高效。
代码示例:
import pandas as pd # 计算每个类别的出现频率(归一化后的值,也可以用原始计数) freq_map = train_df['Item_Identifier'].value_counts(normalize=True).to_dict() # 替换原特征列,生成新的数值列 train_df['Item_Identifier_freq'] = train_df['Item_Identifier'].map(freq_map)
优势:零维度膨胀,计算快,不需要依赖目标变量,适合无监督或快速验证场景。
2. 目标编码(有监督场景首选)
如果你的任务是有监督学习(比如回归、分类),目标编码是个好选择——用每个类别对应的目标变量的均值(或中位数)来编码。不过要注意过拟合问题,最好用交叉验证的方式计算编码值,避免模型记住训练集的噪声。
基础版(快速验证)
# 假设你的目标列叫'target' target_mean_map = train_df.groupby('Item_Identifier')['target'].mean().to_dict() train_df['Item_Identifier_target'] = train_df['Item_Identifier'].map(target_mean_map)
稳健交叉验证版(避免过拟合)
import numpy as np from sklearn.model_selection import KFold kf = KFold(n_splits=5, shuffle=True, random_state=42) train_df['Item_Identifier_target_cv'] = np.nan # 用5折交叉验证,每次用训练折的均值编码验证折 for train_idx, val_idx in kf.split(train_df): train_part = train_df.iloc[train_idx] val_part = train_df.iloc[val_idx] fold_mean_map = train_part.groupby('Item_Identifier')['target'].mean() train_df.loc[val_idx, 'Item_Identifier_target_cv'] = val_part['Item_Identifier'].map(fold_mean_map) # 用全局均值填充可能的缺失值(比如只在某一折出现的稀有类别) global_target_mean = train_df['target'].mean() train_df['Item_Identifier_target_cv'] = train_df['Item_Identifier_target_cv'].fillna(global_target_mean)
优势:直接把类别和目标变量的关联信息融入编码,模型能更好捕捉有用信号,效果通常优于频率编码。
3. 嵌入编码(深度学习场景首选)
如果你的模型是深度学习架构(比如MLP、CNN),可以用嵌入层来学习类别对应的低维向量表示。先给每个类别分配唯一ID,再让模型自动学习合适的嵌入向量。
代码示例(用Keras):
from sklearn.preprocessing import LabelEncoder from tensorflow.keras.layers import Embedding # 先用LabelEncoder给每个类别分配唯一整数ID le = LabelEncoder() train_df['Item_Identifier_id'] = le.fit_transform(train_df['Item_Identifier']) # 在模型中定义Embedding层,比如设置嵌入维度为8 embedding_dim = 8 embedding_layer = Embedding( input_dim=len(le.classes_), # 类别总数 output_dim=embedding_dim, # 嵌入向量维度 input_length=1 # 输入长度(单个特征) )
优势:模型自动学习类别间的语义关联,不需要手动设计编码规则,灵活性高,适合复杂的深度学习任务。
4. 合并低频类别(可选,视数据情况)
如果你的数据里有极少数出现次数特别低的类别,可以把它们合并成一个统一的“Other”类别,再用哑变量或其他编码方式。不过从你的value_counts()来看,大部分类别出现次数都在9-10次,这个方法收益可能不大,但如果有个别类别出现次数远低于这个值,还是值得试试。
代码示例:
# 设定阈值,比如把出现次数小于9的类别合并为'Other' threshold = 9 category_counts = train_df['Item_Identifier'].value_counts() low_freq_categories = category_counts[category_counts < threshold].index.tolist() train_df['Item_Identifier_grouped'] = train_df['Item_Identifier'].replace(low_freq_categories, 'Other') # 之后可以对分组后的列做哑变量编码 dummy_cols = pd.get_dummies(train_df['Item_Identifier_grouped'], prefix='Item') train_df = pd.concat([train_df, dummy_cols], axis=1)
总结
- 无监督/快速验证:选频率编码
- 有监督学习:优先目标编码(交叉验证版)
- 深度学习:用嵌入编码
- 绝对不要用哑变量,不然维度爆炸+矩阵奇异,模型根本跑不起来
内容的提问来源于stack exchange,提问作者jade

