在 df1 中删除也在 df2 中的行的可靠方法

Posted

技术标签:

【中文标题】在 df1 中删除也在 df2 中的行的可靠方法【英文标题】:Reliable way of dropping rows in df1 which are also in df2 【发布时间】:2020-02-27 23:23:15 【问题描述】:

我有一个场景,我有一个 existing 数据框,我有一个 new 数据框,其中包含可能在 existing 框架中但也可能有新行的行。我一直在努力寻找一种可靠的方法,通过将 new 数据帧与 existing 数据帧进行比较来删除这些现有行。

我已经完成了我的作业。解决方案似乎是使用isin()。但是,我发现这有隐患。特别是:

pandas get rows which are NOT in other dataframe

Pandas cannot compute isin with a duplicate axis

Pandas promotes int to float when filtering

有没有一种方法可以根据另一个数据帧中的成员资格/包含从一个数据帧中可靠地过滤掉行?下面显示了一个不捕获极端情况的简单用例。请注意,我想删除 existing 中的 new 中的行,以便 new 仅包含不在 existing 中的行。使用new 中的新行更新existing 的更简单问题可以通过pd.merge() + DataFrame.drop_duplicates() 实现

In [53]: df1 = pd.DataFrame(data = 'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14])  
    ...: df2 = pd.DataFrame(data = 'col1' : [1, 2, 3], 'col2' : [10, 11, 12])                                                                                             

In [54]: df1                                                                                                                                                                
Out[54]: 
   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14

In [55]: df2                                                                                                                                                                
Out[55]: 
   col1  col2
0     1    10
1     2    11
2     3    12

In [56]: df1[~df1.isin(df2)]                                                                                                                                                
Out[56]: 
   col1  col2
0   NaN   NaN
1   NaN   NaN
2   NaN   NaN
3   4.0  13.0
4   5.0  14.0

In [57]: df1[~df1.isin(df2)].dropna()                                                                                                                                       
Out[57]: 
   col1  col2
3   4.0  13.0
4   5.0  14.0

【问题讨论】:

Anti-Join Pandas的可能重复 df1[~df1.isin(df2).all(1)] ? @konvas 我不确定 - 我可以使用合并 + drop_duplicates()。但是,我需要一种方法来仅保留 new 中的新行 - 例如,我可能想要保留这些行。因此,它不仅仅是更新existing - 它也是关于让new 只留下不在existing 中的新行 @anky_91 这和df1[~df1.isin(df2)].dropna()有什么不同 对不起,如果我没有得到这个问题,如果你不想考虑行序列,下面的答案就是你想要的 【参考方案1】:

您可以尝试系列isin。它独立于index。即,它只检查值。您只需要将每个数据帧的列转换为一系列元组即可创建掩码

s1 = df1.agg(tuple, axis=1)
s2 = df2.agg(tuple, axis=1)

df1[~s1.isin(s2)]

Out[538]:
   col1  col2
3     4    13
4     5    14

【讨论】:

这也很聪明:) +1【参考方案2】:

我们可以使用DataFrame.mergeindicator = True + DataFrame.queryDataFrame.drop

df_filtered=( df1.merge(df2,how='outer',indicator=True)
                 .query("_merge == 'left_only'")
                 .drop('_merge',axis=1) )
print(df_filtered)

   col1  col2
3     4    13
4     5    14

如果现在我们改变第 0 行的值:

df1.iat[0,0]=3

第 0 行不再被过滤

df_filtered=( df1.merge(df2,how='outer',indicator=True)
                 .query("_merge == 'left_only'")
                 .drop('_merge',axis=1) )
print(df_filtered)

   col1  col2
0     3    10
3     4    13
4     5    14

一步一步

df_filtered=( df1.merge(df2,how='outer',indicator=True)
 )
print(df_filtered)
   col1  col2      _merge
0     3    10   left_only
1     2    11        both
2     3    12        both
3     4    13   left_only
4     5    14   left_only
5     1    10  right_only

df_filtered=( df1.merge(df2,how='outer',indicator=True).query("_merge == 'left_only'")
 )
print(df_filtered)
   col1  col2     _merge
0     3    10  left_only
3     4    13  left_only
4     5    14  left_only

df_filtered=( df1.merge(df2,how='outer',indicator=True)
                 .query("_merge == 'left_only'")
                 .drop('_merge',axis=1)
 )
print(df_filtered)
   col1  col2
0     3    10
3     4    13
4     5    14

【讨论】:

以上是关于在 df1 中删除也在 df2 中的行的可靠方法的主要内容,如果未能解决你的问题,请参考以下文章

Pyspark 基于另一个类似的数据框添加或删除数据框中的行

spark:合并两个数据帧,如果两个数据帧中的ID重复,则df1中的行覆盖df2中的行

如何从r中的两个数据框中选择匹配的行

合并(连接)数据框 - 结果中的行太多

删除另一个数据框中存在的行? [复制]

计算R中具有相似值的行的平均值