独热编码后如何保持训练集与测试集的列数及列名一致?
解决训练集与测试集独热编码特征不匹配问题
问题背景
原始数据集规模为82580×30,包含多个字符串列。在训练阶段对Nationality列做独热编码后,测试集出现了训练集未见过的类别(如CAN),同时训练集存在的部分类别(如AUT、CHE)在测试集中缺失,导致训练集和测试集的特征维度、特征列完全不一致。若直接取.values强行拟合模型,虽能得到高准确率但属于违规操作,且这类方法无法在决策树等依赖特征列匹配的模型中生效。
核心解决方案
1. 复用训练阶段的编码器,禁止在测试集重新拟合
机器学习预处理必须遵循训练集拟合、测试集转换的原则:编码器仅在训练集上执行fit操作,保存训练后的编码器,测试集直接使用该编码器的transform方法。这样测试集会自动对齐训练集的特征结构——训练集有的特征列测试集都会保留,测试集缺少的训练集类别对应列填充0,训练集未见过的新类别会被转换为全0编码向量。
2. 配置编码器处理未知类别
初始化OneHotEncoder时设置handle_unknown='ignore',确保遇到训练集未见过的类别时不会抛出错误,而是生成全0的编码向量,避免特征维度突变。
3. 避免冗余特征
原代码中拼接了编码特征与原始特征,导致Age、DaysSinceCreation等列重复,直接使用编码器转换后的结果作为特征矩阵即可,无需保留原始列。
完整修正代码示例
训练阶段代码
import pandas as pd from sklearn.preprocessing import OneHotEncoder from sklearn.compose import make_column_transformer # 加载训练集数据 train_df = pd.DataFrame( {'Nationality': {0: 'DEU', 1: 'PRT', 2: 'PRT', 3: 'PRT', 4: 'FRA', 5: 'DEU', 6: 'CHE', 7: 'DEU', 8: 'GBR', 9: 'AUT', 10: 'PRT', 11: 'FRA', 12: 'OTR', 13: 'GBR', 14: 'ESP', 15: 'PRT', 16: 'OTR', 17: 'PRT', 18: 'ESP', 19: 'AUT'}, 'Age': {0: 27.0, 1: 45.46, 2: 45.46, 3: 58.0, 4: 57.0, 5: 27.0, 6: 49.0, 7: 62.0, 8: 44.0, 9: 61.0, 10: 54.0, 11: 53.0, 12: 50.0, 13: 30.0, 14: 51.0, 15: 45.46, 16: 40.0, 17: 49.0, 18: 49.0, 19: 14.0}, 'DaysSinceCreation': {0: 370, 1: 213, 2: 206, 3: 1018, 4: 835, 5: 52, 6: 597, 7: 217, 8: 999, 9: 1004, 10: 402, 11: 879, 12: 393, 13: 923, 14: 249, 15: 52, 16: 159, 17: 929, 18: 49, 19: 131}, 'BookingsCheckedIn': {0: 1, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1, 7: 2, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1, 14: 1, 15: 0, 16: 0, 17: 1, 18: 1, 19: 0}} ) # 定义编码器,配置未知类别处理策略 transformer = make_column_transformer( (OneHotEncoder(sparse=False, handle_unknown='ignore'), ['Nationality']), remainder='passthrough' ) # 拆分特征与标签,仅在训练集拟合编码器并转换 X_train = transformer.fit_transform(train_df.drop('BookingsCheckedIn', axis=1)) y_train = train_df['BookingsCheckedIn'].values # 验证训练集特征结构 X_train_df = pd.DataFrame(X_train, columns=transformer.get_feature_names_out()) print(f"训练集编码后形状: {X_train_df.shape}") print(f"训练集特征列: {X_train_df.columns.tolist()}")
测试阶段代码
# 加载测试集数据 test_df = pd.DataFrame( {'Nationality': {0: 'CAN', 1: 'DEU', 2: 'PRT', 3: 'PRT', 4: 'FRA'}, 'Age': {0: 27.0, 1: 29.0, 2: 24.0, 3: 24.0, 4: 46.0}, 'DaysSinceCreation': {0: 222, 1: 988, 2: 212, 3: 685, 4: 1052}, 'BookingsCheckedIn': {0: 0, 1: 1, 2: 1, 3: 1, 4: 0}} ) # 使用训练好的编码器转换测试集,不重新拟合 X_test = transformer.transform(test_df.drop('BookingsCheckedIn', axis=1)) y_test = test_df['BookingsCheckedIn'].values # 验证测试集特征与训练集对齐 X_test_df = pd.DataFrame(X_test, columns=transformer.get_feature_names_out()) print(f"测试集编码后形状: {X_test_df.shape}") print(f"训练/测试集特征维度一致: {X_train.shape[1] == X_test.shape[1]}") print(f"测试集特征列: {X_test_df.columns.tolist()}")
效果说明
- 训练集和测试集的特征维度完全一致,特征列一一对应
- 测试集中的新类别
CAN会被转换为全0的编码向量,不会新增特征列 - 测试集缺失的训练集类别(如
AUT、CHE)对应的特征列会保留,值为0 - 所有模型(决策树、逻辑回归、神经网络等)都能正常使用,无需依赖违规操作
内容的提问来源于stack exchange,提问作者Mystic
相关产品推荐
相关产品推荐

