如何从一个pandas数据框中减去另一行?

19 投票
4 回答
37111 浏览
提问于 2025-04-18 04:10

我想做的操作有点像合并。比如说,使用 inner 合并,我们会得到一个数据框,这个数据框里包含了在第一个和第二个数据框中都存在的行。而使用 outer 合并,我们会得到一个数据框,这个数据框里包含了在第一个或第二个数据框中存在的行。

我需要的是一个数据框,这个数据框里包含了在第一个数据框中存在但在第二个数据框中不存在的行。有没有什么快速又优雅的方法来实现这个呢?

4 个回答

2

我建议在合并时使用参数'indicator'。另外,如果'on'参数没有指定,它会默认使用两个数据框中列的交集。

new = df1.merge(df2,how='left', indicator=True) # adds a new column '_merge'
new = new[(new['_merge']=='left_only')].copy() #rows only in df1 and not df2
new = new.drop(columns='_merge').copy()

    Team    Year    foo
0   Hawks   2001    5
1   Hawks   2004    4
2   Nets    1987    3
4   Nets    2001    8
5   Nets    2000    10

参考链接: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.merge.html

indicator : boolean or string, default False

If True, adds a column to output DataFrame called “_merge” with information on the source of each row. 
Information column is Categorical-type and takes on a value of 
“left_only” for observations whose merge key only appears in ‘left’ DataFrame,
“right_only” for observations whose merge key only appears in ‘right’ DataFrame, 
and “both” if the observation’s merge key is found in both.
7

如果你的非索引列中有单元格是NaN(缺失值),你可能会遇到错误。

print df1

    Team   Year  foo
0   Hawks  2001    5
1   Hawks  2004    4
2    Nets  1987    3
3    Nets  1988    6
4    Nets  2001    8
5    Nets  2000   10
6    Heat  2004    6
7  Pacers  2003   12
8 Problem  2112  NaN


print df2

     Team  Year  foo
0  Pacers  2003   12
1    Heat  2004    6
2    Nets  1988    6
3 Problem  2112  NaN

new = df1.merge(df2,on=['Team','Year'],how='left')
print new[new.foo_y.isnull()]

     Team  Year  foo_x  foo_y
0   Hawks  2001      5    NaN
1   Hawks  2004      4    NaN
2    Nets  1987      3    NaN
4    Nets  2001      8    NaN
5    Nets  2000     10    NaN
6 Problem  2112    NaN    NaN

在2112年,问题团队在两个表中都没有foo的值。所以这里的左连接会错误地返回这一行,虽然在两个数据框中都有匹配,但它会显示在右侧数据框中不存在。

解决办法:

我的做法是给内部数据框添加一个唯一的列,并为所有行设置一个值。这样在连接时,你可以检查这个列在内部表中是否是NaN,以此来找到外部表中的唯一记录。

df2['in_df2']='yes'

print df2

     Team  Year  foo  in_df2
0  Pacers  2003   12     yes
1    Heat  2004    6     yes
2    Nets  1988    6     yes
3 Problem  2112  NaN     yes


new = df1.merge(df2,on=['Team','Year'],how='left')
print new[new.in_df2.isnull()]

     Team  Year  foo_x  foo_y  in_df1  in_df2
0   Hawks  2001      5    NaN     yes     NaN
1   Hawks  2004      4    NaN     yes     NaN
2    Nets  1987      3    NaN     yes     NaN
4    Nets  2001      8    NaN     yes     NaN
5    Nets  2000     10    NaN     yes     NaN

注意:问题行现在被正确过滤掉了,因为它在in_df2中有一个值。

  Problem  2112    NaN    NaN     yes     yes
27

考虑以下内容:

  1. df_one 是第一个数据框
  2. df_two 是第二个数据框

第一个数据框中存在,但在第二个数据框中不存在的内容

解决方案:通过索引 df = df_one[~df_one.index.isin(df_two.index)]

索引可以替换为你想要排除的特定。在上面的例子中,我使用了索引作为两个数据框之间的参考

另外,你还可以使用更复杂的查询,通过布尔型的 pandas.Series 来解决上述问题。

20

你觉得下面这样怎么样?

print df1

    Team  Year  foo
0   Hawks  2001    5
1   Hawks  2004    4
2    Nets  1987    3
3    Nets  1988    6
4    Nets  2001    8
5    Nets  2000   10
6    Heat  2004    6
7  Pacers  2003   12

print df2

    Team  Year  foo
0  Pacers  2003   12
1    Heat  2004    6
2    Nets  1988    6

只要有一个非关键的、名字相同的列,你就可以让添加的后缀来完成这个工作(如果没有这样的非关键列,你可以临时创建一个... 比如用 df1['common'] = 1df2['common'] = 1):

new = df1.merge(df2,on=['Team','Year'],how='left')
print new[new.foo_y.isnull()]

     Team  Year  foo_x  foo_y
0  Hawks  2001      5    NaN
1  Hawks  2004      4    NaN
2   Nets  1987      3    NaN
4   Nets  2001      8    NaN
5   Nets  2000     10    NaN

或者你可以使用 isin,但你需要创建一个单独的关键列:

df1['key'] = df1['Team'] + df1['Year'].astype(str)
df2['key'] = df1['Team'] + df2['Year'].astype(str)
print df1[~df1.key.isin(df2.key)]

     Team  Year  foo         key
0   Hawks  2001    5   Hawks2001
2    Nets  1987    3    Nets1987
4    Nets  2001    8    Nets2001
5    Nets  2000   10    Nets2000
6    Heat  2004    6    Heat2004
7  Pacers  2003   12  Pacers2003

撰写回答