Python Pandas - 找出两个数据帧之间的差异

use*_*Geo 50 python dataframe pandas

我有两个数据帧df1和df2,其中df2是df1的子集.如何获得两个数据帧之间差异的新数据帧(df3)?

换句话说,一个数据框中df1中的所有行/列都不在df2中?

在此输入图像描述

WeN*_*Ben 71

通过使用 drop_duplicates

pd.concat([df1,df2]).drop_duplicates(keep=False)
Run Code Online (Sandbox Code Playgroud)

Update :

Above method only working for those dataframes they do not have duplicate itself, For example

df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
df2=pd.DataFrame({'A':[1],'B':[2]})
Run Code Online (Sandbox Code Playgroud)

它输出如下,这是错误的

输出错误:

pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]: 
   A  B
1  2  3
Run Code Online (Sandbox Code Playgroud)

正确的输出

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4
Run Code Online (Sandbox Code Playgroud)

怎么实现呢?

方法1:使用isintuple

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]: 
   A  B
1  2  3
2  3  4
3  3  4
Run Code Online (Sandbox Code Playgroud)

方法2:mergeindicator

df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both']
Out[421]: 
   A  B     _merge
1  2  3  left_only
2  3  4  left_only
3  3  4  left_only
Run Code Online (Sandbox Code Playgroud)

  • 您还可以确定在查找重复项时要考虑哪些列:`pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)` (7认同)
  • 您能解释一下“apply(tuple,1)”的含义吗? (3认同)
  • @Szpaqn 注意这个方法不会处理特殊情况。:-) (2认同)
  • 方法 2(`indicator=True`)是一个非常通用且有用的工具,我很乐意在这个答案的顶部看到它,但使用“外部”而不是“左”连接来涵盖所有 3 种情况。 (2认同)

emm*_*phd 16

Pandas 现在提供了一个新的 API来进行数据帧比较:pandas.DataFrame.compare

df.compare(df2)
  col1       col3
  self other self other
0    a     c  NaN   NaN
2  NaN   NaN  3.0   4.0
Run Code Online (Sandbox Code Playgroud)


jpp*_*jpp 13

对于行,请尝试以下操作,并cols设置为要比较的列的列表:

m = df1.merge(df2, on=cols, how='outer', suffixes=['', '_'], indicator=True)
Run Code Online (Sandbox Code Playgroud)

对于列,请尝试以下操作:

set(df1.columns).symmetric_difference(df2.columns)
Run Code Online (Sandbox Code Playgroud)

  • 唐纳德关心评论?具有`indicator = True`的`merge`是用于比较给定字段的数据帧的经典解决方案。 (3认同)

toe*_*r42 12

接受的答案方法 1 不适用于内部包含 NaN 的数据帧,因为pd.np.nan != pd.np.nan. 我不确定这是否是最好的方法,但可以通过以下方式避免

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
Run Code Online (Sandbox Code Playgroud)

它更慢,因为它需要将数据转换为字符串,但多亏了这种转换pd.np.nan == pd.np.nan

让我们来看看代码。首先,我们将值转换为字符串,并将tuple函数应用于每一行。

df1.astype(str).apply(tuple, 1)
df2.astype(str).apply(tuple, 1)
Run Code Online (Sandbox Code Playgroud)

多亏了这一点,我们得到了pd.Series带有元组列表的对象。每个元组包含来自df1/ 的整行df2。然后我们应用isin方法df1来检查每个元组是否“在” df2。结果是pd.Series布尔值。如果元组 fromdf1在 中,则为真df2。最后,我们用~符号否定结果,并在 上应用过滤器df1。长话短说,我们只得到那些df1不在df2.

为了使其更具可读性,我们可以将其写为:

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
Run Code Online (Sandbox Code Playgroud)


Che*_*llo 8

也许是更简单的一行,具有相同或不同的列名称。即使 df2['Name2'] 包含重复值也能工作。

newDf = df1.set_index('Name1')
           .drop(df2['Name2'], errors='ignore')
           .reset_index(drop=False)
Run Code Online (Sandbox Code Playgroud)

  • 简单有效。添加了errors='ignore'来解决目标值不在源中(即交集)的情况的问题,并且最终重置索引会带来与原始值类似的df。 (2认同)

Ogh*_*hli 8

pandas 中有一种新方法DataFrame.compare可以比较 2 个不同的数据帧并返回数据记录每列中更改的值。

例子

第一个数据框

Id Customer Status      Date
1      ABC   Good  Mar 2023
2      BAC   Good  Feb 2024
3      CBA    Bad  Apr 2022
Run Code Online (Sandbox Code Playgroud)

第二个数据框

Id Customer Status      Date
1      ABC    Bad  Mar 2023
2      BAC   Good  Feb 2024
5      CBA   Good  Apr 2024
Run Code Online (Sandbox Code Playgroud)

比较数据框

print("Dataframe difference -- \n")
print(df1.compare(df2))

print("Dataframe difference keeping equal values -- \n")
print(df1.compare(df2, keep_equal=True))

print("Dataframe difference keeping same shape -- \n")
print(df1.compare(df2, keep_shape=True))

print("Dataframe difference keeping same shape and equal values -- \n")
print(df1.compare(df2, keep_shape=True, keep_equal=True))
Run Code Online (Sandbox Code Playgroud)

结果

Dataframe difference -- 

    Id       Status            Date          
  self other   self other      self     other
0  NaN   NaN   Good   Bad       NaN       NaN
2  3.0   5.0    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping equal values -- 

    Id       Status            Date          
  self other   self other      self     other
0    1     1   Good   Bad  Mar 2023  Mar 2023
2    3     5    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping same shape -- 

    Id       Customer       Status            Date          
  self other     self other   self other      self     other
0  NaN   NaN      NaN   NaN   Good   Bad       NaN       NaN
1  NaN   NaN      NaN   NaN    NaN   NaN       NaN       NaN
2  3.0   5.0      NaN   NaN    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping same shape and equal values -- 

    Id       Customer       Status            Date          
  self other     self other   self other      self     other
0    1     1      ABC   ABC   Good   Bad  Mar 2023  Mar 2023
1    2     2      BAC   BAC   Good  Good  Feb 2024  Feb 2024
2    3     5      CBA   CBA    Bad  Good  Apr 2022  Apr 2024
Run Code Online (Sandbox Code Playgroud)


lia*_*gli 7

edit2,我想出了一个不需要设置索引的新解决方案

newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)
Run Code Online (Sandbox Code Playgroud)

好的,我发现最高投票的答案已经包含了我所想的。是的,我们只能在每两个 dfs 中没有重复的条件下使用此代码。


我有一个棘手的方法。首先,我们将“名称”设置为问题给出的两个数据帧的索引。由于我们在两个 dfs 中有相同的“名称”,我们可以从“较大”的 df 中删除“较小”的 df 索引。这是代码。

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)
Run Code Online (Sandbox Code Playgroud)

  • 你可能的意思是 pd.concat([df1,df2]).drop_duplicates(keep=False) (2认同)

Spe*_*er5 5

import pandas as pd
# given
df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',],
    'Age':[23,45,12,34,27,44,28,39,40]})
df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',],
    'Age':[23,12,34,44,28,40]})

# find elements in df1 that are not in df2
df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True)

# output:
print('df1\n', df1)
print('df2\n', df2)
print('df_1notin2\n', df_1notin2)

# df1
#     Age   Name
# 0   23   John
# 1   45   Mike
# 2   12  Smith
# 3   34   Wale
# 4   27  Marry
# 5   44    Tom
# 6   28  Menda
# 7   39   Bolt
# 8   40  Yuswa
# df2
#     Age   Name
# 0   23   John
# 1   12  Smith
# 2   34   Wale
# 3   44    Tom
# 4   28  Menda
# 5   40  Yuswa
# df_1notin2
#     Age   Name
# 0   45   Mike
# 1   27  Marry
# 2   39   Bolt
Run Code Online (Sandbox Code Playgroud)


Luc*_*hko 5

除了接受的答案之外,我还想提出一种更广泛的解决方案,可以找到两个数据帧与任何/的2D 集合差异(它们对于两个数据帧可能不一致)。该方法还允许为数据帧比较设置元素的容差(它使用)indexcolumnsfloatnp.isclose


import numpy as np
import pandas as pd

def get_dataframe_setdiff2d(df_new: pd.DataFrame, 
                            df_old: pd.DataFrame, 
                            rtol=1e-03, atol=1e-05) -> pd.DataFrame:
    """Returns set difference of two pandas DataFrames"""

    union_index = np.union1d(df_new.index, df_old.index)
    union_columns = np.union1d(df_new.columns, df_old.columns)

    new = df_new.reindex(index=union_index, columns=union_columns)
    old = df_old.reindex(index=union_index, columns=union_columns)

    mask_diff = ~np.isclose(new, old, rtol, atol)

    df_bool = pd.DataFrame(mask_diff, union_index, union_columns)

    df_diff = pd.concat([new[df_bool].stack(),
                         old[df_bool].stack()], axis=1)

    df_diff.columns = ["New", "Old"]

    return df_diff
Run Code Online (Sandbox Code Playgroud)

例子:

In [1]

df1 = pd.DataFrame({'A':[2,1,2],'C':[2,1,2]})
df2 = pd.DataFrame({'A':[1,1],'B':[1,1]})

print("df1:\n", df1, "\n")

print("df2:\n", df2, "\n")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:\n", diff, "\n")
Run Code Online (Sandbox Code Playgroud)
Out [1]

df1:
   A  C
0  2  2
1  1  1
2  2  2 

df2:
   A  B
0  1  1
1  1  1 

diff:
     New  Old
0 A  2.0  1.0
  B  NaN  1.0
  C  2.0  NaN
1 B  NaN  1.0
  C  1.0  NaN
2 A  2.0  NaN
  C  2.0  NaN 
Run Code Online (Sandbox Code Playgroud)