用于分类功能的LabelEncoder?


狗头军师
2025-03-11 04:04:41 (28天前)

这可能是一个初学者的问题,但是我已经看到很多人使用LabelEncoder()用常规替换类别变量。许多人通过一次传递多个列来使用此功能,但是我对某些功能中的错误序数及其对模型的影响会产生疑问。这是一个例子:

输入值


  1. import pandas as pd
    import numpy as np
    from sklearn.preprocessing import LabelEncoder

    a = pd.DataFrame([‘High’,’Low’,’Low’,’Medium’])
    le = LabelEncoder()
    le.fit_transform(a)

输出量

array([0, 1, 1, 2], dtype=int64)
如您所见,序数值未正确映射,因为我的LabelEncoder仅关心列/数组中的顺序(应该为High = 1,Med = 2,Low = 3,反之亦然)。错误的严重映射会如何影响模型?除了OrdinalEncoder()之外,还有其他简便的方法可以正确映射这些值吗?

2 条回复
  1. 1# v-star*위위 | 2020-08-23 15-22

    TL; DR:使用a LabelEncoder编码序数 形式的任何特征都是一个坏主意!

    实际上,这在文档中有明确说明,其中提到,顾名思义,该编码方法旨在对标签进行编码:

    该转换器应用于编码目标值,即y,而不是输入 X。

    正如您在问题中正确指出的那样,将序数要素的固有序数映射到错误的比例将对模型的性能产生非常负面的影响(即与要素的相关性成比例)。这同样适用于一个明确的功能,只是原来的功能没有序数。

    一种思考的直观方法是决策树设置其边界。在训练过程中,决策树将学习在每个节点处设置的最佳功能以及最佳阈值,根据这些值,看不见的样本将沿着一个分支或另一个分支前进。

    如果我们用简单的编码序数特征LabelEncoder,可能会导致特征1表示温暖,2可能转化为热并0代表沸腾。在这种情况下,结果将最终成为具有不必要的大量拆分的树,因此,对于更简单的建模而言,其复杂性要高得多。

    相反,正确的方法是使用OrdinalEncoder,并为序数特征定义适当的映射方案。或者,在具有分类功能的情况下,我们应该查看OneHotEncoder或分类编码器中可用的各种编码器。

    尽管实际上了解到为什么这是一个坏主意,将比仅凭文字更直观。

    让我们使用一个简单的示例来说明上述内容,该示例由两个序数功能组成,其中包含一个范围,该范围包括学生准备考试的时数和所有以前作业的平均成绩,以及一个目标变量,指示考试是否已过或不。我将数据框的列定义为

    1. pd.Categorical
    2. df = pd.DataFrame(
    3. {'Hours of dedication': pd.Categorical(
    4. values = ['25-30', '20-25', '5-10', '5-10', '40-45',
    5. '0-5', '15-20', '20-25', '30-35', '5-10',
    6. '10-15', '45-50', '20-25'],
    7. categories=['0-5', '5-10', '10-15', '15-20',
    8. '20-25', '25-30','30-35','40-45', '45-50']),
    9. 'Assignments avg grade': pd.Categorical(
    10. values = ['B', 'C', 'F', 'C', 'B',
    11. 'D', 'C', 'A', 'B', 'B',
    12. 'B', 'A', 'D'],
    13. categories=['F', 'D', 'C', 'B','A']),
    14. 'Result': pd.Categorical(
    15. values = ['Pass', 'Pass', 'Fail', 'Fail', 'Pass',
    16. 'Fail', 'Fail','Pass','Pass', 'Fail',
    17. 'Fail', 'Pass', 'Pass'],
    18. categories=['Fail', 'Pass'])
    19. }
    20. )

    如前所述,将分类列定义为熊猫的分类的好处是我们可以在其类别之间建立顺序。这允许基于已建立的顺序而不是词法排序更快地进行排序。而且,它还可以用作一种简单的方法来根据其顺序获取不同类别的代码。

    因此,我们将使用的数据帧如下所示:

    1. print(df.head())
    2. Hours_of_dedication Assignments_avg_grade Result
    3. 0 20-25 B Pass
    4. 1 20-25 C Pass
    5. 2 5-10 F Fail
    6. 3 5-10 C Fail
    7. 4 40-45 B Pass
    8. 5 0-5 D Fail
    9. 6 15-20 C Fail
    10. 7 20-25 A Pass
    11. 8 30-35 B Pass
    12. 9 5-10 B Fail

    可以通过以下方式获得相应的类别代码:

    1. X = df.apply(lambda x: x.cat.codes)
    2. X.head()
    3. Hours_of_dedication Assignments_avg_grade Result
    4. 0 4 3 1
    5. 1 4 2 1
    6. 2 1 0 0
    7. 3 1 2 0
    8. 4 7 3 1
    9. 5 0 1 0
    10. 6 3 2 0
    11. 7 4 4 1
    12. 8 6 3 1
    13. 9 1 3 0

    现在让我们拟合一个DecisionTreeClassifier,看看树如何定义拆分:

    1. from sklearn import tree
    2. dt = tree.DecisionTreeClassifier()
    3. y = X.pop('Result')
    4. dt.fit(X, y)
    5. 我们可以使用plot_tree以下方式可视化树结构:
    6. t = tree.plot_tree(dt,
    7. feature_names = X.columns,
    8. class_names=["Fail", "Pass"],
    9. filled = True,
    10. label='all',
    11. rounded=True)

    在此处输入图片说明

    这就是全部??好吧… 是的!实际上,我对功能的设置方式是,“奉献时数”功能与考试是否通过之间存在一种简单而明显的联系,这清楚地表明,该问题应该非常容易建模。

    现在,让我们尝试通过使用我们可以通过例如获得的编码方案直接编码所有特征来进行相同的操作LabelEncoder,因此不考虑特征的实际顺序,而只是随机分配一个值:

    1. df_wrong = df.copy()
    2. df_wrong['Hours_of_dedication'].cat.set_categories(
    3. ['0-5','40-45', '25-30', '10-15', '5-10', '45-50','15-20',
    4. '20-25','30-35'], inplace=True)
    5. df_wrong['Assignments_avg_grade'].cat.set_categories(
    6. ['A', 'C', 'F', 'D', 'B'], inplace=True)
    7. rcParams['figure.figsize'] = 14,18
    8. X_wrong = df_wrong.drop(['Result'],1).apply(lambda x: x.cat.codes)
    9. y = df_wrong.Result
    10. dt_wrong = tree.DecisionTreeClassifier()
    11. dt_wrong.fit(X_wrong, y)
    12. t = tree.plot_tree(dt_wrong,
    13. feature_names = X_wrong.columns,
    14. class_names=["Fail", "Pass"],
    15. filled = True,
    16. label='all',
    17. rounded=True)

    在此处输入图片说明

    不出所料,树结构比我们要建模的简单问题复杂得多。为了使树正确地预测所有训练样本,它已扩展到深度为4,此时单个节点就足够了。

    这将暗示分类器可能过拟合,因为我们正在急剧增加复杂性。通过修剪树并调整必要的参数以防止过度拟合,我们也无法解决问题,因为我们通过错误地编码特征而添加了太多噪声。

    因此,总而言之,一旦对特征进行编码,就必须保留其通常性,否则至关重要,否则如本例所示,我们将失去其所有可预测的功能,而只会给模型增加噪声。

登录 后才能参与评论