值得一看
双11 12
广告
广告

解决LabelEncoder在训练/测试数据中遇到未知标签的ValueError

解决labelencoder在训练/测试数据中遇到未知标签的valueerror

在使用sklearn.preprocessing.LabelEncoder对分类特征进行数值化编码时,一个常见的挑战是当测试集中出现训练集中未曾见过的标签时,会引发ValueError: y contains previously unseen labels。此错误的核心在于LabelEncoder的工作机制:它在fit阶段学习并映射训练数据中的所有唯一标签到整数。如果在transform阶段遇到一个在fit时未见过的标签,由于没有对应的映射规则,便会抛出此错误。与OneHotEncoder等其他编码器不同,LabelEncoder本身并没有内置的handle_unknown参数来直接处理这种情况。

问题分析:为何会出现“未知标签”错误?

在机器学习项目中,通常会将数据集划分为训练集和测试集。当对分类特征进行编码时,一个常见的误区是分别对训练集和测试集进行编码,或者仅在训练集上拟合编码器,然后直接应用于测试集。

考虑以下伪代码片段,它展示了导致问题的典型操作模式:

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的解决方案步骤:

  1. 确定需要编码的分类列。
  2. 为每个分类列初始化一个LabelEncoder实例。
  3. 对于每个分类列:

    • 收集所有唯一标签: 遍历训练集和测试集中所有相关(相同二级列名)的MultiIndex列,将这些列中的所有唯一值汇集起来。
    • 拟合编码器: 使用汇集到的所有唯一标签来拟合对应的LabelEncoder实例。
  4. 转换数据: 使用已经拟合好的编码器,分别转换训练集和测试集中的相应列。

示例代码

为了清晰地演示,我们首先创建模拟的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()都能正确地处理,因为它已经“见过”所有可能的标签。

注意事项与最佳实践

  1. 数据类型转换 (.astype(str)): 在应用LabelEncoder之前,通常建议将目标列转换为字符串类型 (.astype(str))。这可以避免因列中存在混合数据类型(例如字符串和数字)或NaN值而导致的潜在错误或意外行为。LabelEncoder默认情况下对NaN的处理可能不是预期行为,将其转换为字符串通常能统一处理。
  2. 数据泄漏(Data Leakage): 尽管为了LabelEncoder的目的将训练集和测试集数据合并以拟合编码器是必要的且安全的,但在其他预处理步骤(如特征缩放、缺失值填充)中,直接合并数据可能会导致数据泄漏。数据泄漏是指模型在训练阶段接触到了测试集的信息,从而导致对模型性能的乐观估计。对于LabelEncoder,由于它只学习标签的映射关系,而不是统计属性,因此这种合并通常不会导致数据泄漏。
  3. 替代方案: 如果在某些情况下,测试集中出现真正的“未知”标签(即这些标签在训练集和任何已知数据集中都未出现),且您希望将其视为一个特殊类别而不是抛出错误,可以考虑使用sklearn.preprocessing.OrdinalEncoder。OrdinalEncoder提供了handle_unknown=’use_encoded_value’和unknown_value参数,允许您为未知标签指定一个特定的编码值(例如-1),从而避免错误。然而,对于LabelEncoder,上述的“预先拟合所有已知标签”策略
温馨提示: 本文最后更新于2025-07-19 22:28:58,某些文章具有时效性,若有错误或已失效,请在下方留言或联系易赚网
文章版权声明 1 本网站名称: 创客网
2 本站永久网址:https://new.ie310.com
1 本文采用非商业性使用-相同方式共享 4.0 国际许可协议[CC BY-NC-SA]进行授权
2 本站所有内容仅供参考,分享出来是为了可以给大家提供新的思路。
3 互联网转载资源会有一些其他联系方式,请大家不要盲目相信,被骗本站概不负责!
4 本网站只做项目揭秘,无法一对一教学指导,每篇文章内都含项目全套的教程讲解,请仔细阅读。
5 本站分享的所有平台仅供展示,本站不对平台真实性负责,站长建议大家自己根据项目关键词自己选择平台。
6 因为文章发布时间和您阅读文章时间存在时间差,所以有些项目红利期可能已经过了,能不能赚钱需要自己判断。
7 本网站仅做资源分享,不做任何收益保障,创业公司上收费几百上千的项目我免费分享出来的,希望大家可以认真学习。
8 本站所有资料均来自互联网公开分享,并不代表本站立场,如不慎侵犯到您的版权利益,请联系79283999@qq.com删除。

本站资料仅供学习交流使用请勿商业运营,严禁从事违法,侵权等任何非法活动,否则后果自负!
THE END
喜欢就支持一下吧
点赞5赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容