Pandas:如何处理测试集中训练集未观测到的分类特征
处理测试集出现训练集未观测分类特征的解决方案
我来给你梳理几个靠谱的方法,解决你遇到的「测试集出现训练集没见过的分类特征,导致独热编码后特征不匹配」的问题,结合你的例子一步步说:
首先回顾下你的场景:训练集是这个DataFrame
import pandas as pd old = pd.DataFrame({"car":["Audi", "BMW", "Mazda"]})
用pd.get_dummies(old)编码后,得到car_Audi、car_BMW、car_Mazda三个特征列。但如果测试集出现比如Tesla这种训练集没有的类别,直接编码就会多出新列,和训练集的特征维度不匹配,模型根本没法用。
下面是几种实用的解决办法:
方法1:用训练集的特征列强制对齐(快速上手)
核心思路是先把训练集编码后的列名存下来,处理测试集时,只保留这些列,缺失的列补0,多余的列直接删掉。
代码示例:
# 第一步:处理训练集并保存特征列 old = pd.DataFrame({"car":["Audi", "BMW", "Mazda"]}) train_dummies = pd.get_dummies(old) train_feature_cols = train_dummies.columns.tolist() # 存下训练集的特征列 # 第二步:处理测试集 test = pd.DataFrame({"car":["Audi", "Tesla", "BMW"]}) test_dummies = pd.get_dummies(test) # 第三步:对齐特征列——保留训练集有的列,缺失补0,多余删除 test_aligned = test_dummies.reindex(columns=train_feature_cols, fill_value=0) print(test_aligned)
输出结果里,Tesla对应的行三个特征列全是0,完美匹配训练集的特征维度。
方法2:用sklearn的OneHotEncoder(工业界规范做法)
sklearn的OneHotEncoder自带handle_unknown='ignore'参数,专门解决这个问题——遇到训练集没见过的类别时,直接输出全0的编码,不会新增特征列。
代码示例:
from sklearn.preprocessing import OneHotEncoder import pandas as pd # 用训练集拟合编码器 old = pd.DataFrame({"car":["Audi", "BMW", "Mazda"]}) encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore') encoder.fit(old) # 关键:必须用训练集拟合,不能碰测试集 # 处理测试集 test = pd.DataFrame({"car":["Audi", "Tesla", "Mazda"]}) test_encoded = encoder.transform(test) # 转成DataFrame方便查看 test_encoded_df = pd.DataFrame(test_encoded, columns=encoder.get_feature_names_out()) print(test_encoded_df)
这个方法更规范,适合构建完整的机器学习流水线,避免手动处理的疏漏。
方法3:自定义类别映射(适合类别少的场景)
如果你的分类类别不多,可以手动给每个训练集类别分配编码,遇到未知类别时统一映射成全0(或者你也可以专门加一个car_Unknown列,把未知类别映射到这个列)。
代码示例:
import pandas as pd # 手动定义训练集类别的编码映射 car_code_map = { "Audi": [1, 0, 0], "BMW": [0, 1, 0], "Mazda": [0, 0, 1] } # 处理训练集 old = pd.DataFrame({"car":["Audi", "BMW", "Mazda"]}) old_encoded = old["car"].apply(lambda x: pd.Series(car_code_map[x])) old_encoded.columns = ["car_Audi", "car_BMW", "car_Mazda"] # 处理测试集——未知类别用[0,0,0]填充 test = pd.DataFrame({"car":["Audi", "Tesla", "BMW"]}) test_encoded = test["car"].apply(lambda x: pd.Series(car_code_map.get(x, [0,0,0]))) test_encoded.columns = ["car_Audi", "car_BMW", "car_Mazda"] print(test_encoded)
这种方法灵活性高,适合你对类别有明确控制权的小数据集。
额外提醒
- 绝对不要用测试集拟合编码器:如果在测试集上重新拟合编码,会导致数据泄露,模型的泛化能力会严重下降。
- 如果未知类别出现频率很高:那你可能需要重新审视训练集,考虑补充这些类别的数据,或者换用更鲁棒的特征编码方式(比如目标编码时也要单独处理未知类别,但独热编码用上面的方法就足够)。
内容的提问来源于stack exchange,提问作者anon_swe
相关产品推荐
相关产品推荐

