【问题标题】:Compare DataFrames for Different Rows regardless of row order无论行顺序如何,比较不同行的 DataFrame
【发布时间】:2021-02-11 10:19:07
【问题描述】:

我一直在尝试比较两个数据框以查找缺失的行和不同的行:

案例1:行数相同,行数不同:

在这种情况下,我的行数相同,但有两个不同的行:

dict_a = {'Values 1':[15, 2, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grapes"]}
dict_b = {'Values 1':[15, 3, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grape"]}

df1 = pd.DataFrame(dict_a)
df2 = pd.DataFrame(dict_b)

所以,我可以使用以下方法找到不同的行索引:

list(df1[~df1.isin(df2)].dropna(how = 'all').index)

这导致[1, 5]

案例2:行数不同,行数不同:

在这种情况下,我有不同的行数和两个不同的行。

dict_a = {'Values 1':[15, 2, 3, 24, 1], 'Values 2':[10, 7, 3, 5, 6], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana"]}
dict_b = {'Values 1':[15, 1, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grape"]}

df1 = pd.DataFrame(dict_a)
df2 = pd.DataFrame(dict_b)

display(df1)
display(df2)

在这里,我首先检查缺失的行。由此,我可以通过比较长度较大的数据帧和较短的长度来发现它是第五个索引:

df2[~df2.index.isin(df1.index)]

我还可以找到不同的行:

df1[~df1.isin(df2)].dropna(how = 'all')

它们是[1, 4]

案例 3:行互换,但行数据相同

dict_a = {'Values 1':[2, 15, 3, 24, 5, 16], 'Values 2':[7, 10, 3, 5, 6, 23], 
          'Values 3': ["Orange", "Apple", "Kiwi", "Cherry", "Banana", "Grape"]}
dict_b = {'Values 1':[15, 2, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grape"]}

df1 = pd.DataFrame(dict_a)
df2 = pd.DataFrame(dict_b)

display(df1)
display(df2)

但是,该代码不适用于互换的行。无论位置如何,它都需要检查行是否已经存在并返回结果“没有不同的行”,但它却说索引[0, 1] 处的行不同。

所以,我正在寻找一种解决方案,它可以比较两个数据帧,而不管行顺序或位置、缺失行和不同行。

所以,我的目标是比较两个数据框并返回“缺失的行”和“不同的行”,即它们彼此不完全匹配。有没有这样一个函数可以真正用这种方式比较行?

【问题讨论】:

  • 有很多方法可以做到这一点,我认为在您的用例中,如果列对齐,请使用 pd.concat([df1,df2],keys['df1','df2']) 然后您可以使用 .drop_duplicates 及其不同的参数来获得所需的行集(请参阅文档中的keep=False, keep='first',keep='last')。另一种可行的方法是根据某些字段创建一个唯一的哈希键,然后在该键上使用您的isin 方法来查看增量。

标签: python pandas dataframe


【解决方案1】:

这是我偶然学到的一个绝妙技巧 pd.merge 很好地展示了这一点

dict_a = {'Values 1':[2, 15, 3, 24, 5, 16], 'Values 2':[7, 10, 3, 5, 6, 23], 
          'Values 3': ["Orange", "Apple", "Kiwi", "Cherry", "Banana", "Grape"]}
dict_b = {'Values 1':[15, 2, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grape"]}

df1 = pd.DataFrame(dict_a)
df2 = pd.DataFrame(dict_b)

df3 = pd.merge(df1,df2,how='outer',on=['Values 1','Values 2','Values 3'],indicator=True)
print(df3)

是你可以看到的最后一个案例,这解决了订单问题

   Values 1  Values 2 Values 3 _merge
0         2         7   Orange   both
1        15        10    Apple   both
2         3         3     Kiwi   both
3        24         5   Cherry   both
4         5         6   Banana   both
5        16        23    Grape   both

现在回到之前的案例

dict_c = {'Values 1':[15, 2, 3, 24, 5, 16], 'Values 2':[10, 7, 3, 5, 6, 23], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Nut"]}
df4 = pd.DataFrame(dict_c)
df5 = pd.merge(df1,df4,how='outer', on=['Values 1','Values 2','Values 3'],indicator=True)
print(df5)

你得到

   Values 1  Values 2 Values 3      _merge
0         2         7   Orange        both
1        15        10    Apple        both
2         3         3     Kiwi        both
3        24         5   Cherry        both
4         5         6   Banana        both
5        16        23    Grape   left_only
6        16        23      Nut  right_only

指标会告诉您它们的不同之处。使用 on 变量还可以使您可以使用列的子集来解决您的问题。

【讨论】:

  • indicator 是救命稻草。但是如果数据框很大,那么在所有列上进行合并会很昂贵
  • 我是否可以将其用作适用于所有情况的多合一解决方案,即比较任何数据帧?如果是这样,你能告诉我最后如何找到“缺失的行”和“不同的行”吗?
  • 关于不同与缺失之间存在真正的问题;如果你有两个项目其中一个拼写错误,比如螺栓和印迹,那么你会得到两个缺失或两个不同?我用它来获得三个组,两个组中的组,df1 中而不是 df2 中的组,以及 df2 中而不是 df1 中的组。你可以考虑,如果你只在左边,它们在右边就不见了。但是当你在这两者中都缺少它的困难。
  • @Paul:是的,我打算用一张桌子作为我的原件,另一张用来检查它是否与原件保持同步。所以,从这个意义上说,我想要么是how='left',要么是how='right'
【解决方案2】:

对于缺失的行,只需使用 drop duplicates 和 concat 即可

import pandas as pd
dict_a = {'Values 1':[2, 15, 3, 24, 5, 16, 2], 'Values 2':[7, 10, 3, 5, 6, 23,1], 
          'Values 3': ["Orange", "Apple", "Kiwi", "Cherry", "Banana", "Grape", "test"]}
dict_b = {'Values 1':[15, 2, 3, 24, 5, 16, 1], 'Values 2':[10, 7, 3, 5, 6, 23, 0], 
          'Values 3': ["Apple", "Orange", "Kiwi", "Cherry", "Banana", "Grape", "test"]}

df1 = pd.DataFrame(dict_a)
df2 = pd.DataFrame(dict_b)
pd.concat([df1, df2]).drop_duplicates(keep=False)

对于不同的行,我建议以下逻辑。 您比较整个数据框并检查行的总和不等于 3 的位置。

df1[(df1 == df2).apply(sum, axis = 1) != 3] 

df2[(df1 == df2).apply(sum, axis = 1) != 3] 

【讨论】:

    【解决方案3】:

    你可以使用集合 -

    s1 = set(df1.to_string(index=False, header=False).split('\n'))
    s2 = set(df2.to_string(index=False, header=False).split('\n'))
    for item in s1.union(s2):
        if item in s1.intersection(s2):
            print(f'{item}|both')
        elif item in s1:
            print(f'{item}|left_only')
        else:
            print(f'{item}|right_only')
    #  1   7  Orange|right_only
    #  5   6  Banana|right_only
    # 24   5  Cherry|both
    #  2   7  Orange|left_only
    # 15  10   Apple|both
    # 16  23   Grape|right_only
    #  3   3    Kiwi|both
    #  1   6  Banana|left_only
    

    【讨论】:

      猜你喜欢
      • 2019-10-01
      • 2016-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 2011-05-09
      • 1970-01-01
      相关资源
      最近更新 更多