当行可以包含多个值时,等效于数字编码

数据挖掘 机器学习 特征工程 编码
2022-03-10 12:13:21

如果我们有这样的列:

    Name
0  Alice
1    Bob
2   Dave

然后,经过数字编码后,变为:

    Name
0  0
1  1
2  2

但是,如果我们有这样的列怎么办:

              Names
0        Alice, Bob
1  Alice, Bob, Dave
2              Dave

编码它的一种方法是这样的:

   Alice  Bob  Dave
0      1    1     0
1      1    1     1
2      0    0     1

但是,这会产生很多额外的列。有没有办法以一种不会导致大量额外列的方式对这样的列进行编码,就像使用数字编码而不是单热编码可以防止太多列出现在我的第一个列中显示?

如果您使用的是 Python,这里有一些代码可以重现我的 DataFrame:

import pandas as pd
df = pd.DataFrame({'Names': ['Alice, Bob', 'Alice, Bob, Dave', 'Dave']})

编辑:这样做的最终目的是通过一个基于树的分类器。

4个回答

二进制编码似乎是最自然的,准确地捕捉当前关系而不引入不存在的关系。然后,名称的组合被认为是超立方体的顶点;自然,如果您将其分解为一维,您将引入不存在的关系。事实上,这就是标签编码所发生的情况:Bob 与 Alice 的关系并不比 Dave 更接近,但您的标签编码使情况看起来如此。

我通常对二进制编码感到满意,所以我没有太多支持以下内容,但有一些想法:

对于您的情况,您仍然可以将超立方体投影到一维;有很多方法可以做到这一点,每一种方法都会添加一些不应该存在的额外信息。您可以尝试并尝试找到最适合您的目的的一种。

这听起来应该让人想起词嵌入,并建议了另一种方法:使用神经网络生成“实体嵌入”,即从超立方体到一些较小维度空间的投影。通过这种方式,您可以发现对您的问题有用的人的组合(也许您最终会得到“爱丽丝和戴夫中的至少一个”等特征)。您还可以做一些更透明的事情,并手动进行一些特征工程来挑选有趣的组合和分箱,以保持特征的数量可控。为此,您可以考虑对每个组合进行目标编码。(确实,这本身就是一个非常简单的解决方案,尽管如果你有很多人或一些罕见的人组合,目标编码很容易导致过度拟合。)

最后,您也许可以将包含 (Alice, Dave) 的行分成两部分,一个是 Alice,另一个是 Dave。您现在可以进行标签编码(或其他任何方式),并且需要一个能够理解每个样本点的多行的模型,或者一个处理组合行的后处理方法。

我认为,这取决于你有多少独特的价值,以及接下来你想用这些数据做什么。中间表目前似乎是不错的解决方案,但您预计会产生开销。因此,也许它适合您以单热方式(二进制编码)创建位掩码。

为了减少 onehot 编码产生的列数,一种解决方案可能是target encoding也就是说,如果您有下表:

   Alice  Bob  Dave Other_features Target
0      1    1     0             X1      0
1      1    1     0             X2      1
2      1    1     0             X3      0
3      1    1     1             X4      1
4      1    1     1             X5      1
5      1    1     1             X6      1
6      0    0     1             X7      0
7      0    0     1             X8      0

您可以创建一个表:

Group               Group_Avg_target
Alice,Bob                      0.333
Alice, Bob, Dave                   1
Dave                               0

然后替换数据集中的值:

   Group_Avg_target Other_features Target
0             0.333             X1      0
1             0.333             X2      1
2             0.333             X3      0
3                 1             X4      1
4                 1             X5      1
5                 1             X6      1
6                 0             X7      0
7                 0             X8      0

这样你最终会得到一列。如果您有足够的实例可以按人群获得有意义的平均值,这主要是有效的。我一般不建议使用它,因为你会失去很多人与人之间的互动,这可能很难解释发生了什么。

注意:要使用此解决方案,您需要确保没有“目标泄漏”,即使用火车上计算的平均值来编码您的测试数据集。

首先,对您的Name列执行 OneHotEncoder 操作,这将为您提供如上所述的三列,即AliceBobDave

完成后我们有这个表OneHotEncoding

    Alice  Bob  Dave
0   1      1    0     
1   1      1    1   
2   0      0    1     

现在您可以apply在这些列上调用函数;传递以逐行axis=1使用函数,然后将其转换然后它们如下(函数将输入转换为给定的数据类型):applydtypestrjoinastype

df['AliceBobDave']= df[df.columns[0:]].apply(lambda x: ','.join(x.dropna().astype(int).astype(str)),axis=1)

以上将为您提供输出:

     Alice  Bob  Dave  AliceBobDave
0    1      1    0     1,1,0
1    1      1    1     1,1,1
2    0      0    1     0,0,1

现在在下面运行此命令以删除columns最终数据帧中不需要的。例如。在这种情况下,我们删除Alice, Bob, 并Dave使用droppandas 中的函数:

df = df.drop(['Alice','Bob','Dave'], axis = 1)

df执行此命令后,如果我们使用打印print(df),我们会得到:

    AliceBobDave
0   1,1,0
1   1,1,1
2   0,0,1

这是您想要的输出。