
在机器学习项目中,通常会将数据集划分为训练集和测试集。当对分类特征进行编码时,一个常见的误区是分别对训练集和测试集进行编码,或者仅在训练集上拟合编码器,然后直接应用于测试集。
考虑以下伪代码片段,它展示了导致问题的典型操作模式:
import pandas as pd
from sklearn.preprocessing import LabelEncoder
# 假设 train_data 和 test_data 是 MultiIndex DataFrame
# 假设 categoricals 是需要编码的列名列表,例如 ['TRAITS', 'UNIT_1']
le = {col: LabelEncoder() for col in categoricals}
# 错误的编码方式示例
# train_data 和 test_data 结构类似原始问题中的 MultiIndex
# train_data[(col, sub_col)] 是一个 Series
# test_data[(col, sub_col)] 是一个 Series
for col_level0 in train_data.columns.levels[0]: # 例如 'TEAM ONE', 'TEAM TWO'
for col_level1 in train_data.columns.levels[1]: # 例如 'TRAITS', 'UNIT_1'
if col_level1 in le: # 如果当前二级列名是需要编码的分类列
# 在训练数据上拟合并转换
train_data[(col_level0, col_level1)] = le[col_level1].fit_transform(
train_data[(col_level0, col_level1)].astype(str)
)
# 在测试数据上直接转换
test_data[(col_level0, col_level1)] = le[col_level1].transform(
test_data[(col_level0, col_level1)].astype(str)
)上述代码的问题在于,对于每个sub_col(例如TRAITS),LabelEncoder实例le[sub_col]在循环内部的train_data[(col_level0, sub_col)].fit_transform()这一行被重复拟合。这意味着,当col_level0从'TEAM ONE'变为'TEAM TWO'时,le['TRAITS']会再次被拟合,其内部的标签映射会被train_data[('TEAM TWO', 'TRAITS')]中的标签覆盖或更新。更严重的是,当处理test_data时,如果test_data[(col_level0, sub_col)]中包含任何在le[sub_col]最后一次fit操作(即在train_data[(last_col_level0, sub_col)]上拟合)中未曾见过的标签,就会触发ValueError。
理想情况下,LabelEncoder应该在看到所有可能的标签后进行一次性拟合,无论这些标签出现在训练集还是测试集中。
解决ValueError的关键在于确保LabelEncoder在执行任何转换之前,已经学习了所有可能出现的标签,包括训练集和测试集中的标签。最直接有效的方法是,对于每一个需要编码的分类列,收集其在训练集和测试集中的所有唯一值,然后用这些唯一值来拟合对应的LabelEncoder。
以下是针对MultiIndex DataFrame的解决方案步骤:
为了清晰地演示,我们首先创建模拟的MultiIndex DataFrame数据。
import pandas as pd
from sklearn.preprocessing import LabelEncoder
import numpy as np
# 1. 创建模拟的 MultiIndex DataFrame 数据
# 定义需要编码的分类列(二级列名)
categoricals = ['TRAITS', 'UNIT_1', 'AUGMENT_1']
# 模拟训练数据
train_data_raw = {
('TEAM ONE', 'TRAITS'): ['A', 'B', 'C', 'A', 'B'],
('TEAM ONE', 'UNIT_1'): ['X', 'Y', 'Z', 'X', 'Y'],
('TEAM ONE', 'AUGMENT_1'): ['P', 'Q', 'R', 'P', 'Q'],
('TEAM TWO', 'TRAITS'): ['A', 'C', 'D', 'A', 'C'],
('TEAM TWO', 'UNIT_1'): ['X', 'Z', 'W', 'X', 'Z'],
('TEAM TWO', 'AUGMENT_1'): ['P', 'R', 'S', 'P', 'R'],
}
train_df = pd.DataFrame(train_data_raw)
train_df.columns = pd.MultiIndex.from_tuples(train_df.columns)
# 模拟测试数据 (故意包含训练集未见的标签,如 'E', 'F', 'V', 'T', 'G', 'H', 'U', 'K')
test_data_raw = {
('TEAM ONE', 'TRAITS'): ['A', 'B', 'E', 'A', 'F'],
('TEAM ONE', 'UNIT_1'): ['X', 'Y', 'V', 'X', 'Y'],
('TEAM ONE', 'AUGMENT_1'): ['P', 'Q', 'T', 'P', 'Q'],
('TEAM TWO', 'TRAITS'): ['A', 'C', 'G', 'A', 'H'],
('TEAM TWO', 'UNIT_1'): ['X', 'Z', 'U', 'X', 'Z'],
('TEAM TWO', 'AUGMENT_1'): ['P', 'R', 'K', 'P', 'R'],
}
test_df = pd.DataFrame(test_data_raw)
test_df.columns = pd.MultiIndex.from_tuples(test_df.columns)
print("原始训练数据:\n", train_df)
print("\n原始测试数据:\n", test_df)
# 2. 初始化 LabelEncoder 字典
le = {col: LabelEncoder() for col in categoricals}
# 3. 正确的编码方法:在所有数据上拟合编码器
train_df_encoded = train_df.copy()
test_df_encoded = test_df.copy()
for sub_col_name in categoricals:
# 收集当前二级列名在所有一级列(TEAM ONE, TEAM TWO)和所有数据集(train, test)中的所有唯一值
all_values_for_sub_col = pd.Series(dtype='object')
for team_col in train_df.columns.levels[0]: # 遍历一级列名,如 'TEAM ONE', 'TEAM TWO'
# 检查 MultiIndex 列是否存在,以防数据不完整
if (team_col, sub_col_name) in train_df.columns:
all_values_for_sub_col = pd.concat([all_values_for_sub_col, train_df[(team_col, sub_col_name)].astype(str)])
if (team_col, sub_col_name) in test_df.columns:
all_values_for_sub_col = pd.concat([all_values_for_sub_col, test_df[(team_col, sub_col_name)].astype(str)])
# 使用所有收集到的唯一值来拟合对应的 LabelEncoder
# .unique() 确保只传递唯一值,提高效率
le[sub_col_name].fit(all_values_for_sub_col.unique())
# 4. 使用拟合好的编码器转换训练集和测试集
for team_col in train_df_encoded.columns.levels[0]:
if (team_col, sub_col_name) in train_df_encoded.columns:
train_df_encoded[(team_col, sub_col_name)] = le[sub_col_name].transform(
train_df_encoded[(team_col, sub_col_name)].astype(str)
)
if (team_col, sub_col_name) in test_df_encoded.columns:
test_df_encoded[(team_col, sub_col_name)] = le[sub_col_name].transform(
test_df_encoded[(team_col, sub_col_name)].astype(str)
)
print("\n编码后的训练数据 (正确方法):\n", train_df_encoded)
print("\n编码后的测试数据 (正确方法):\n", test_df_encoded)
# 验证逆转换(可选)
# print("\n逆转换示例 (TEAM ONE, TRAITS 在测试数据中):")
# print(le['TRAITS'].inverse_transform(test_df_encoded[('TEAM ONE', 'TRAITS')]))在上述代码中,我们首先遍历categoricals列表中的每个二级列名(例如'TRAITS')。然后,对于'TRAITS'这个列,我们从train_df和test_df中所有一级列('TEAM ONE'和'TEAM TWO')下的'TRAITS'列中收集所有的唯一值。一旦收集到所有可能的标签,我们便使用le['TRAITS'].fit()来拟合编码器。此后,无论是对训练集还是测试集中的'TRAITS'列进行转换,le['TRAITS'].transform()都能正确地处理,因为它已经“见过”所有可能的标签。
以上就是解决LabelEncoder在训练/测试数据中遇到未知标签的ValueError的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号