【问题标题】:Pandas Dataframe Find Rows Where all Columns EqualPandas Dataframe 查找所有列相等的行
【发布时间】:2014-03-28 00:11:41
【问题描述】:

我有一个包含字符的数据框 - 我想要一个逐行的布尔结果,告诉我该行的所有列是否具有相同的值。

例如,我有

df = [  a   b   c   d

0  'C'   'C'   'C'   'C' 

1  'C'   'C'   'A'   'A'

2  'A'   'A'   'A'   'A' ]

我希望结果是

0  True

1  False

2  True

我试过 .all 但似乎我只能检查是否所有都等于一个字母。我能想到的唯一另一种方法是在每一行上做一个唯一的,看看它是否等于 1?提前致谢。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    我认为最简洁的方法是使用 eq: 对照第一列检查所有列:

    In [11]: df
    Out[11]: 
       a  b  c  d
    0  C  C  C  C
    1  C  C  A  A
    2  A  A  A  A
    
    In [12]: df.iloc[:, 0]
    Out[12]: 
    0    C
    1    C
    2    A
    Name: a, dtype: object
    
    In [13]: df.eq(df.iloc[:, 0], axis=0)
    Out[13]: 
          a     b      c      d
    0  True  True   True   True
    1  True  True  False  False
    2  True  True   True   True
    

    现在你可以使用 all(如果它们都等于第一项,则它们都相等):

    In [14]: df.eq(df.iloc[:, 0], axis=0).all(1)
    Out[14]: 
    0     True
    1    False
    2     True
    dtype: bool
    

    【讨论】:

    • 这对我和我们来说似乎是最直观的方式。谢谢。
    • 最好写成df.eq(df.iloc[:, 0], axis=0).all(axis=1)
    • the documentation 的注释:“不匹配的索引将合并在一起。NaN 值被认为是不同的(即 NaN != NaN)。”
    【解决方案2】:

    按第一列比较 array 并检查每行是否所有 Trues:

    在 numpy 中使用相同的解决方案以获得更好的性能:

    a = df.values
    b = (a == a[:, [0]]).all(axis=1)
    print (b)
    [ True  True False]
    

    如果需要Series:

    s = pd.Series(b, axis=df.index)
    

    比较解决方案:

    data = [[10,10,10],[12,12,12],[10,12,10]]
    df = pd.DataFrame(data,columns=['Col1','Col2','Col3'])
    
    #[30000 rows x 3 columns]
    df = pd.concat([df] * 10000, ignore_index=True)
    

    #jez - numpy array
    In [14]: %%timeit
        ...: a = df.values
        ...: b = (a == a[:, [0]]).all(axis=1)
    141 µs ± 3.23 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    #jez - Series 
    In [15]: %%timeit
        ...: a = df.values
        ...: b = (a == a[:, [0]]).all(axis=1)
        ...: pd.Series(b, index=df.index)
    169 µs ± 2.02 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    #Andy Hayden
    In [16]: %%timeit
        ...: df.eq(df.iloc[:, 0], axis=0).all(axis=1)
    2.22 ms ± 68.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    #Wen1
    In [17]: %%timeit
        ...: list(map(lambda x : len(set(x))==1,df.values))
    56.8 ms ± 1.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    #K.-Michael Aye
    In [18]: %%timeit
        ...: df.apply(lambda x: len(set(x)) == 1, axis=1)
    686 ms ± 23.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    #Wen2    
    In [19]: %%timeit
        ...: df.nunique(1).eq(1)
    2.87 s ± 115 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    【讨论】:

    • 为什么DataFrame.apply() 与地图相比如此缓慢(Aye vs. Wen1)?两者都为每一行运行相同的 lambda。造成这种开销的原因是什么?
    • pandas == 1.1.5 numpy == 1.19.5Python == 3.8 下,对于5,000,000 行和6 列int 数据,前3 种方法的效率几乎相同,而df.eq 的效率提高了20%。
    • @Travis - 是的,有可能,答案是 3 岁。但我现在可以再次测试。
    • 运行您的示例数据,jez - numpy array 是 78µs,jez - Series 是 150µs,df.eq 是 593µs。这可能取决于不同的dtypesshape 数据
    • @Travis - 是的,同意,我的数据是数字。
    【解决方案3】:

    nunique: 0.20.0 版中的新功能。(基于来自 Jez 的时间 benchmark,如果性能不重要,你可以使用这个)

    df.nunique(axis = 1).eq(1)
    Out[308]: 
    0     True
    1    False
    2     True
    dtype: bool
    

    或者你可以使用mapset

    list(map(lambda x : len(set(x))==1,df.values))
    

    【讨论】:

    • 一个重要的事情是记住nunique方法中的dropna参数。如果某些行中有 NaN,默认情况下 nunique 不会计算它们,上面会显示值相等。我们必须设置dropna=False 来避免这种情况。所以在这种情况下正确的行是df.nunique(axis = 1, dropna=False).eq(1)
    【解决方案4】:
    df = pd.DataFrame.from_dict({'a':'C C A'.split(),
                            'b':'C C A'.split(),
                            'c':'C A A'.split(),
                            'd':'C A A'.split()})
    df.apply(lambda x: len(set(x)) == 1, axis=1)
    0     True
    1    False
    2     True
    dtype: bool
    

    解释:如果行的所有元素都相同,则 set(x) 只有 1 个元素。 axis=1 选项将任何给定的函数应用于行。

    【讨论】:

      【解决方案5】:

      您可以使用nunique(axis=1),因此可以通过以下方式获得结果(添加到新列):

      df['unique'] = df.nunique(axis=1) == 1
      

      @yo-and-ben-w 的答案使用eq(1),但我认为== 1 更容易阅读。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-09
        • 2021-11-04
        • 2023-04-07
        • 2022-08-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        相关资源
        最近更新 更多