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

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


当前回答

另一个可能的解决方案是使用numpy广播:

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

其他回答

对于行,尝试这样做,其中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)

定义数据框架:

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

地点:

isin(df2)返回df1中也在df2中的行。 ~(元素逻辑NOT)在表达式前面对结果求反,因此我们得到df1中不在df2中的元素——两者之间的差值。 .dropna()删除NaN显示所需输出的行

注意:这只适用于len(df1) >= len(df2)。如果df2比df1长,可以反转表达式:df2[~df2.isin(df1)].dropna()

对称差分

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

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

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

设置差分/关系代数差分

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

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

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

通过使用drop_duplicate

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

更新:

上面的方法只适用于那些本身没有副本的数据帧。例如:

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

如何实现这一目标?

方法一:将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

方法二:与指标合并

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
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\n', df1)
print('df2\n', df2)
print('df_1notin2\n', 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