在 pandas 中将行中的最大值设置为 1,其余设置为 0

Posted

技术标签:

【中文标题】在 pandas 中将行中的最大值设置为 1,其余设置为 0【英文标题】:Setting highest value in row to 1 and rest to 0 in pandas 【发布时间】:2018-11-05 07:11:34 【问题描述】:

我的原始数据框如下所示:

A       B       C
0.10    0.83    0.07
0.40    0.30    0.30
0.70    0.17    0.13    
0.72    0.04    0.24    
0.15    0.07    0.78    

我希望每一行都被二值化:1 将分配给具有最高值的列,其余的将设置为 0,因此前一个数据帧将变为:

A   B   C
0   1   0
1   0   0
1   0   0   
1   0   0   
0   0   1   

如何做到这一点? 谢谢。

编辑:我知道一个特定的案例使我的问题模棱两可。我应该说,如果给定行的 3 列相等,我仍然希望获得一个 [1 0 0] 向量,而不是该行的 [1 1 1]。

【问题讨论】:

你如何对待像0.333, 0.333, 0.333这样的列? 鉴于我在数据框中的浮点精度,我认为不会出现这样的问题。但如果发生这种情况,我不介意将“1”随机分配给三列之一,其余为 0。 如果你没有任何重复,你可以做== max()(然后输入它/乘以1/随便)。如果确实有重复项,并且想随机选择一个而不是将它们都设置为 1,那就有点复杂了。 请注意,人们给您的几乎所有答案都是== max() 的变体。因为您暗示的唯一可能不是您想要的地方是在评论中。您确实需要将问题编辑为明确的。 @abarnert:我不确定你的意思?给出的解决方案真的很令人满意,解决了我的问题。您是在谈论编辑以添加如果一行的三列重复,我只希望一个等于 1 吗? 【参考方案1】:

使用 numpyargmax

m = np.zeros_like(df.values)
m[np.arange(len(df)), df.values.argmax(1)] = 1

df1 = pd.DataFrame(m, columns = df.columns).astype(int)

# Result


   A  B  C
0  0  1  0
1  1  0  0
2  1  0  0
3  1  0  0
4  0  0  1

时间

df_test = df.concat([df] * 1000)

def chris_z(df):
     m = np.zeros_like(df.values)
     m[np.arange(len(df)), df.values.argmax(1)] = 1
     return pd.DataFrame(m, columns = df.columns).astype(int)

def haleemur(df):
    return df.apply(lambda x: x == x.max(), axis=1).astype(int)

def haleemur_2(df):
    return pd.DataFrame((df.T == df.T.max()).T.astype(int), columns=df.columns)

def sacul(df):
    return pd.DataFrame(np.where(df.T == df.T.max(), 1, 0),index=df.columns).T

结果

In [320]: %timeit chris_z(df_test)
358 µs ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [321]: %timeit haleemur(df_test)
1.14 s ± 45.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [329]: %timeit haleemur_2(df_test)
972 µs ± 11.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [333]: %timeit sacul(df_test)
1.01 ms ± 3.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

【讨论】:

您可能希望 m 为布尔型,不是吗? argmax 解决方案是唯一(至少可以说)对重复项正确且最快的解决方案。【参考方案2】:

另一个numpy方法,使用np.where

import numpy as np
new_df = pd.DataFrame(np.where(df.T == df.T.max(), 1, 0),index=df.columns).T
   A  B  C
0  0  1  0
1  1  0  0
2  1  0  0
3  1  0  0
4  0  0  1

【讨论】:

如果有重复,这会给出错误的结果。虽然这个问题对于在这种情况下应该发生的事情模棱两可,但从 OP 的 cmets 看来,1 1 1 行是不可接受的。 另外,np.where 只是增加了开销,将时间与@haleemur 的相同答案进行比较,没有np.where 实际上更快。 我不会删除,它仍然是一个有效的答案。这个问题很模棱两可 谢谢大家的cmets,确实是我的错。让我先编辑我的问题。对不起:) 在我的时间里查看haleemur_2 与您的解决方案,这就是我所说的开销。它对性能几乎没有影响【参考方案3】:
 df.apply(lambda x: x == x.max(), axis=1).astype(int) 

应该这样做。这通过检查该值是否是该列的最大值,然后转换为整数 (True -> 1, False -> 0)

除了apply-ing a lambda row-wise,还可以转置数据帧并与max比较,然后转回

(df.T == df.T.max()).T.astype(int)

最后,一个非常快速的基于 numpy 的解决方案:

pd.DataFrame((df.T.values == np.amax(df.values, 1)).T*1, columns = df.columns)

所有情况下的输出:

   A  B  C
0  0  1  0
1  1  0  0
2  1  0  0
3  1  0  0
4  0  0  1

【讨论】:

这没有给我正确的输出,它使第 1 行和第 2 行全为零 哦,我看错了问题,您需要按行进行最大比较。相应地更新。 @chrisz,已修复。 您的第二种方法要快得多 它会和我的方法一样快,如果不是稍微快一点的话,但同样,如果两列共享相同的值,它将返回重复的1s

以上是关于在 pandas 中将行中的最大值设置为 1,其余设置为 0的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python/Pandas 中将变量设置为“今天”日期

Pandas groupby 使用选择行中的时间窗口

在 HiveQL 中将变量设置为最大值

pandas使用drop_duplicates函数基于subset参数指定的数据列子集删除重复行并设置keep参数保留重复行中的最后一个数据行

Pandas 最大行数,前 n 最大

如何检查一系列字符串是不是包含在 PANDAS DataFrame 列中并将该字符串分配为行中的新列?