当我搜索这个主题时,我得到的答案与我想要做的不匹配。假设我有一张这样的表格:

物品 N1 氮气 N3 N4
项目1 1 2 4 8
项目2 2 3 6 7
项目3 4 5 7 9
项目4 1 5 6 7
项目5 3 4 7 8

我想对其进行独热编码以获得:

物品 1 2 3 4 5 6 7 8 9
项目1 1 1 0 1 0 0 0 1 0
项目2 0 1 1 0 0 1 1 0 0
项目3 0 0 0 1 1 0 1 0 1
项目4 1 0 0 0 1 1 1 0 0
项目5 0 0 1 1 0 0 1 1 0

这可行吗?我现在正在编写某种循环来遍历每一行,但我决定询问是否有人知道更有效的方法来做到这一点。


最佳答案
3

使用 melt 和 crosstab。

tmp = df.melt('Item')
result = pd.crosstab(tmp['Item'], tmp['value']).reset_index().rename_axis(None, axis=1)

    Item  1  2  3  4  5  6  7  8  9
0  Item1  1  1  0  1  0  0  0  1  0
1  Item2  0  1  1  0  0  1  1  0  0
2  Item3  0  0  0  1  1  0  1  0  1
3  Item4  1  0  0  0  1  1  1  0  0
4  Item5  0  0  1  1  0  0  1  1  0

3

  • 1
    使用,您可以使用colnames参数来避免链接df.rename_axis。即pd.crosstab(tmp['Item'], tmp['value'], colnames=[None]).reset_index()


    – 


  • 如果“item”列是 datetimeindex 而不是常规列,该怎么办?显然 datetimeindex 没有属性 melt。


    – 

  • 我无法重现错误… 列是否设置为索引?如果是这样,只需使用 tmp = df.unstack().reset_index(name=’N’) 而不是 melt,然后使用 crosstab


    – 

您可以尝试,例如,

pd.get_dummies(df.melt('Item'), columns=['value'], prefix='', prefix_sep='').drop('variable', axis=1).groupby('Item').sum().reset_index()

由此得出

    Item  1  2  3  4  5  6  7  8  9
0  Item1  1  1  0  1  0  0  0  1  0
1  Item2  0  1  1  0  0  1  1  0  0
2  Item3  0  0  0  1  1  0  1  0  1
3  Item4  1  0  0  0  1  1  1  0  0
4  Item5  0  0  1  1  0  0  1  1  0

数据

df = pd.DataFrame(
    {
        "Item": ["Item1", "Item2", "Item3", "Item4", "Item5"],
        "N1": [1, 2, 4, 1, 3],
        "N2": [2, 3, 5, 5, 4],
        "N3": [4, 6, 7, 6, 7],
        "N4": [8, 7, 9, 7, 8],
    }
)

2

  • 嗨,我是原帖者。我选择了 Triky 的答案,因为它满足了我的要求(即标签是实际数字)。但您的解决方案也很好,我可以想到在某些情况下最好有一个命名标签。谢谢。


    – 

  • @Eric 没问题。你可以查看我的更新


    – 

另一个可能的解决方案是使用sklearn。此解决方案利用sklearn的特定列进行独热编码df。其步骤如下:

  • 首先,它初始化MultiLabelBinarizer并将其方法应用于到的fit_transform列的值,将它们转换为二进制矩阵,其中每个唯一值表示为单独的列。N1N4

  • 然后从这个二进制矩阵创建一个新的数据框,其列以所标识的唯一值命名binarize.classes_

  • 最后,用于将原始Item列与新创建的独热编码数据框相结合。

from sklearn.preprocessing import MultiLabelBinarizer

binarize = MultiLabelBinarizer()
pd.concat(
    [df[['Item']], 
     pd.DataFrame(
         binarize.fit_transform(df[['N1', 'N2', 'N3', 'N4']].values), 
         columns=binarize.classes_)], axis=1)

注意:为了避免枚举以 开头的所有列N,@ThomasIsCoding(我向他表示感谢)在下面的评论中建议使用:

df.loc[:,df.columns.str.startswith("N")]

或者

df.iloc[:,1:]

输出:

    Item  1  2  3  4  5  6  7  8  9
0  Item1  1  1  0  1  0  0  0  1  0
1  Item2  0  1  1  0  0  1  1  0  0
2  Item3  0  0  0  1  1  0  1  0  1
3  Item4  1  0  0  0  1  1  1  0  0
4  Item5  0  0  1  1  0  0  1  1  0

5

  • 1
    这也很好,因为简单的事实就是很容易理解到底发生了什么。


    – 

  • 1
    有趣的方法MultiLabelBinarizer,+1!


    – 

  • 1
    谢谢,@ThomasIsCoding!我现在从事机器学习工作,这启发了我提出这个解决方案。


    – 

  • 1
    哈哈,sklearn确实是个宝藏岛 :D。一个小评论:也许您可以使用df.iloc[:,1:]来避免枚举以N或开头的所有列df.loc[:,df.columns.str.startswith("N")],以防有大量这样的列


    – 

  • 谢谢@ThomasIsCoding的评论!与此同时,我也相应地更新了我的答案。


    –