0

0

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

心靈之曲

心靈之曲

发布时间:2025-07-19 20:32:15

|

345人浏览过

|

来源于php中文网

原创

解决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。

超级简历WonderCV
超级简历WonderCV

免费求职简历模版下载制作,应届生职场人必备简历制作神器

下载

以下是针对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,上述的“预先拟合所有已知标签”策略

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

216

2025.10.31

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

249

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

205

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

609

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

547

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

539

2024.04.29

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号