如何从一个pandas数据框中减去另一行?
我想做的操作有点像合并。比如说,使用 inner
合并,我们会得到一个数据框,这个数据框里包含了在第一个和第二个数据框中都存在的行。而使用 outer
合并,我们会得到一个数据框,这个数据框里包含了在第一个或第二个数据框中存在的行。
我需要的是一个数据框,这个数据框里包含了在第一个数据框中存在但在第二个数据框中不存在的行。有没有什么快速又优雅的方法来实现这个呢?
4 个回答
我建议在合并时使用参数'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.
如果你的非索引列中有单元格是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
考虑以下内容:
- df_one 是第一个数据框
- df_two 是第二个数据框
在第一个数据框中存在,但在第二个数据框中不存在的内容
解决方案:通过索引
df = df_one[~df_one.index.isin(df_two.index)]
索引可以替换为你想要排除的特定列。在上面的例子中,我使用了索引作为两个数据框之间的参考
另外,你还可以使用更复杂的查询,通过布尔型的 pandas.Series 来解决上述问题。
你觉得下面这样怎么样?
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'] = 1
和 df2['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