查找两个数据框之间的差异

2024-12-20 08:37:00
admin
原创
79
摘要:问题描述:我有两个数据框 df1 和 df2,其中 df2 是 df1 的子集。如何获取两个数据框之间的差异的新数据框 (df3)?换句话说,数据框包含 df1 中所有不在 df2 中的行/列?解决方案 1:通过使用drop_duplicatespd.concat([df1,df2]).drop_duplic...

问题描述:

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

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

在此处输入图片描述


解决方案 1:

通过使用drop_duplicates

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

Update :

The above method only works for those data frames that don't already have duplicates themselves. For example:

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

它将输出如下内容,这是错误的

错误输出:

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

正确输出

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4

如何实现这一点?

方法 1:isin使用tuple

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

方法 2:merge使用indicator

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

解决方案 2:

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

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

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

对于列,尝试以下操作:

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

解决方案 3:

接受的答案方法 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.Series一个包含元组列表的对象。每个元组包含来自df1/ 的整行df2。然后我们应用isin方法df1检查每个元组是否“在” 中df2。结果为布尔值。如果来自 的元组在 中,则结果pd.Series为 True 。最后,我们用符号取反结果,然后应用过滤器。长话短说,我们只得到那些不在 中的行。df1`df2~df1df1df2`

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

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]

解决方案 4:

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

解决方案 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
', df1)
print('df2
', df2)
print('df_1notin2
', 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

解决方案 6:

pandas 中有一个新方法DataFrame.compare,可以比较两个不同的数据框并返回数据记录中每列发生变化的值。

例子

第一个数据框

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

第二个数据框

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

比较数据框

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

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

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

print("Dataframe difference keeping same shape and equal values -- 
")
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

解决方案 7:

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

newDf = df1.set_index('Name1')
           .drop(df2['Name2'], errors='ignore')
           .reset_index(drop=False)

解决方案 8:

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

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

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


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

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)

解决方案 9:

除了已接受的答案之外,我想提出一个更广泛的解决方案,可以找到具有任何/ 的两个数据框的2D 集差异(它们可能与两个数据框不一致)。此外,方法还允许设置元素的容差以进行数据框比较(它使用)index`columnsfloatnp.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:
", df1, "
")

print("df2:
", df2, "
")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:
", diff, "
")
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 

解决方案 10:

对称差分

如果您只对其中一个数据框中的行感兴趣,但不对两者同时感兴趣,那么您正在寻找集合差异:

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

⚠️仅当两个数据框都不包含任何重复项时才有效。

集合差分 / 关系代数差分

如果您对关系代数差异/集合差异感兴趣,即df1-df2df1df2

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

⚠️仅当两个数据框都不包含任何重复项时才有效。

解决方案 11:

正如这里提到

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

是正确的解决方案,但如果

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

在这种情况下,上述解决方案将给出
空的数据框,相反,您应该使用concat从每个数据框中删除重复项后的方法。

使用concate with drop_duplicates

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

解决方案 12:

当一侧有重复项,而另一侧至少有一个重复项时,我在处理重复项时会遇到问题,因此我过去Counter.collections会进行更好的 diff,以确保两侧的计数相同。这不会返回重复项,但如果两侧的计数相同,则不会返回任何重复项。

from collections import Counter

def diff(df1, df2, on=None):
    """
    :param on: same as pandas.df.merge(on) (a list of columns)
    """
    on = on if on else df1.columns
    df1on = df1[on]
    df2on = df2[on]
    c1 = Counter(df1on.apply(tuple, 'columns'))
    c2 = Counter(df2on.apply(tuple, 'columns'))
    c1c2 = c1-c2
    c2c1 = c2-c1
    df1ondf2on = pd.DataFrame(list(c1c2.elements()), columns=on)
    df2ondf1on = pd.DataFrame(list(c2c1.elements()), columns=on)
    df1df2 = df1.merge(df1ondf2on).drop_duplicates(subset=on)
    df2df1 = df2.merge(df2ondf1on).drop_duplicates(subset=on)
    return pd.concat([df1df2, df2df1])
> df1 = pd.DataFrame({'a': [1, 1, 3, 4, 4]})
> df2 = pd.DataFrame({'a': [1, 2, 3, 4, 4]})
> diff(df1, df2)
   a
0  1
0  2

解决方案 13:

@liangli 的解决方案略有变化,不需要更改现有数据框的索引:

newdf = df1.drop(df1.join(df2.set_index('Name').index))

解决方案 14:

通过索引查找差异。假设 df1 是 df2 的子集,并且在子集化时索引会被结转

df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

# Example

df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5])

df2 =  df1.loc[[1,3,5]]

df1

 gender subject
1      f     bio
2      m    chem
3      f     phy
4      m     bio
5      f     bio

df2

  gender subject
1      f     bio
3      f     phy
5      f     bio

df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

df3

  gender subject
2      m    chem
4      m     bio

解决方案 15:

定义我们的数据框:

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

df2 = df1[df1.Name.isin(['John','Smith','Wale','Tom','Menda','Yuswa'])

df1

    Name  Age
0   John   23
1   Mike   45
2  Smith   12
3   Wale   34
4  Marry   27
5    Tom   44
6  Menda   28
7   Bolt   39
8  Yuswa   40

df2

    Name  Age
0   John   23
2  Smith   12
3   Wale   34
5    Tom   44
6  Menda   28
8  Yuswa   40

两者的区别在于:

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

    Name   Age
1   Mike  45.0
4  Marry  27.0
7   Bolt  39.0

在哪里:

  • df1.isin(df2)返回 中的行,df1这些行也位于 中df2

  • ~表达式前面的(逐元素逻辑非)对结果取反,因此我们得到了不在的元素df1-两者df2之间的差。

  • .dropna()`NaN`删除显示所需输出的行

注意这仅在 时才有效len(df1) >= len(df2)。如果df2长度超过,df1则可以反转表达式:df2[~df2.isin(df1)].dropna()

解决方案 16:

我发现这个deepdiff库是一个很棒的工具,如果需要不同的细节或顺序很重要,它也可以很好地扩展到数据框。您可以尝试使用 diffing to_dict('records')to_numpy()和其他导出:

import pandas as pd
from deepdiff import DeepDiff

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

df2 = df1[df1.Name.isin(['John','Smith','Wale','Tom','Menda','Yuswa'])]

DeepDiff(df1.to_dict(), df2.to_dict())
# {'dictionary_item_removed': [root['Name'][1], root['Name'][4], root['Name'][7], root['Age'][1], root['Age'][4], root['Age'][7]]}

解决方案 17:

另一个可能的解决方案是使用numpy broadcasting

df1[np.all(~np.all(df1.values == df2.values[:, None], axis=2), axis=0)]

输出:

    Name  Age
1   Mike   45
4  Marry   27
7   Bolt   39

解决方案 18:

_merge使用 lambda 函数,您可以过滤具有值的行以获取所有缺失的“left_only”df1`df2`

df3 = df1.merge(df2, how = 'outer' ,indicator=True).loc[lambda x :x['_merge']=='left_only']
df

解决方案 19:

尝试一下这个:
df_new = df1.merge(df2, how='outer', indicator=True).query('_merge == "left_only"').drop('_merge', 1)

它将产生一个具有差异的新数据框:df1 中存在但不在 df2 中的值。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1000  
  华为作为全球领先的信息与通信技术(ICT)解决方案提供商,其全球化项目的成功离不开高效的项目管理方法。其中,集成产品开发(IPD)流程是华为项目管理体系的核心组成部分。IPD流程不仅帮助华为在复杂的全球化项目中实现了资源的高效整合,还通过跨部门协作和持续优化,确保了项目的高质量交付。本文将通过具体案例,分析华为IPD流...
IPD测试流程   0  
  IPD(Integrated Product Development)是一种以跨职能团队协作为核心的产品开发流程,旨在通过整合资源、优化流程和提高决策效率,实现产品从概念到市场的快速、高效交付。IPD流程的核心思想是将传统的串行开发模式转变为并行开发模式,通过跨部门协作和早期风险识别,减少开发周期中的浪费和返工。这种方...
IPD流程分为几个阶段   0  
  华为的集成产品开发(IPD)流程是企业项目管理中的经典实践,其核心在于通过跨部门协同实现高效的产品开发。IPD流程强调从市场需求到产品交付的全生命周期管理,而跨部门沟通则是这一流程成功的关键。在华为的实践中,跨部门沟通不仅仅是信息的传递,更是团队协作、目标对齐和资源整合的重要手段。本文将深入探讨IPD流程中的跨部门沟通...
IPD项目管理咨询   0  
  IPD流程全称是集成产品开发(Integrated Product Development),它是一种以客户需求为导向、跨部门协作的产品开发模式。与传统产品开发模式相比,IPD强调在产品开发的早期阶段就整合市场、研发、制造、采购等多个部门的资源和能力,通过并行工程和协同工作来提升开发效率。IPD流程的核心在于打破部门壁...
IPD产品开发流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用