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

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


当前回答

pandas DataFrame.compare中有一种新的方法,即比较2个不同的dataframe,并返回数据记录中每列中变化的值。

例子

第一个Dataframe

Id Customer Status      Date
1      ABC   Good  Mar 2023
2      BAC   Good  Feb 2024
3      CBA    Bad  Apr 2022

第二个Dataframe

Id Customer Status      Date
1      ABC    Bad  Mar 2023
2      BAC   Good  Feb 2024
5      CBA   Good  Apr 2024

比较Dataframes

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))

结果

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

其他回答

方法1对于有nan的数据帧无效,因为pd.np.nan != pd.np.nan !我不确定这是否是最好的方法,但它可以避免

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]

它更慢,因为它需要将数据转换为字符串,但由于这个转换pd.np.nan == pd.np.nan。

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

df1.astype(str).apply(tuple, 1)
df2.astype(str).apply(tuple, 1)

多亏了这个,我们得到了pd。具有元组列表的系列对象。每个元组包含df1/df2的整行。 然后我们对df1应用isin方法来检查每个元组是否“在”df2中。 结果是pd。带有bool值的系列。如果tuple from df1在df2中,则为True。最后,我们用~符号对结果求反,并对df1进行滤波。长话短说,我们只能从df1中得到那些不在df2中的行。

为了使它更具可读性,我们可以这样写:

df1_str_tuples = df1.astype(str).apply(tuple, 1)
df2_str_tuples = df2.astype(str).apply(tuple, 1)
df1_values_in_df2_filter = df1_str_tuples.isin(df2_str_tuples)
df1_values_not_in_df2 = df1[~df1_values_in_df2_filter]

对称差分

如果你只对其中一个数据帧中的行感兴趣,而不是两个数据帧中的行,你在寻找集的差异:

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

⚠️只有在两个数据帧都不包含任何重复的情况下才有效。

设置差分/关系代数差分

如果你对关系代数差异/集差异感兴趣,即df1-df2或df1\df2:

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

⚠️只有在两个数据帧都不包含任何重复的情况下才有效。

对于行,尝试这样做,其中Name是联合索引列(可以是多个公共列的列表,或者指定left_on和right_on):

m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)

indicator=True设置很有用,因为它添加了一个名为_merge的列,其中包含df1和df2之间的所有更改,分为3种可能的类型:“left_only”,“right_only”或“both”。

对于列,试试这个:

set(df1.columns).symmetric_difference(df2.columns)

除了公认的答案,我想提出一个更广泛的解决方案,可以找到两个数据框架的2D集差异与任何索引/列(他们可能不符合两个数据框架)。此外,该方法允许设置浮动元素的容忍度,用于数据帧比较(它使用np.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

例子:

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")
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 

试试这个: Df_new = df1。merge(df2, how='outer', indicator=True)。查询('_merge == "left_only"')。下降(_merge, 1)

它将产生一个新的数据框架,其差异是:df1中存在的值,而df2中不存在。