当一方有重复项而另一方至少有一个重复项时,我在处理重复项时遇到了问题,因此我使用了 Counter.collections
进行更好的差异比较,确保两侧具有相同的计数。这不会返回重复项,但如果两侧计数相同,则不会返回任何结果。
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
如此处所述,提到了
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)
df1 [~ df1.apply(tuple,1).isin(df2.apply(tuple,1))]
即使在这种情况下也是正确答案。如果您想获取仅在df1或df2中而不是两者都存在的值,则建议的方法是正确的(但需要从原始数据帧中删除重复项)。 - iradf1.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
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()
我发现 deepdiff
库是一个非常好用的工具,如果需要不同的详细信息或者顺序方面的考虑,对于数据帧也可以进行扩展。你可以试着使用 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]]}
另一个可能的解决方案是使用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
这是@liangli的好方法的轻微变化,不需要更改现有数据帧的索引:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
df_new = df1.merge(df2, how='outer', indicator=True).query('_merge == "left_only"').drop('_merge', 1)
它将生成一个新的数据框,其中包含差异:存在于df1中但不存在于df2中的值。df3 = df1.merge(df2, how = 'outer' ,indicator=True).loc[lambda x :x['_merge']=='left_only']
df
query("_merge == 'left_only'")
代替loc中的lambda表达式。df1.merge(df2, how = 'outer' ,indicator=True).query("_merge == 'left_only'")"
- dimButTries