60

一个简单的熊猫问题:

是否有drop_duplicates()删除复制中涉及的每一行的功能?

一个等效的问题如下:熊猫是否对数据帧有一定的差异?

例如:

In [5]: df1 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})

In [6]: df2 = pd.DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})

In [7]: df1
Out[7]: 
   col1  col2
0     1     2
1     2     3
2     3     4

In [8]: df2
Out[8]: 
   col1  col2
0     4     6
1     2     3
2     5     5

所以也许类似的东西df2.set_diff(df1)会产生这个:

   col1  col2
0     4     6
2     5     5

但是,我不想依赖索引,因为就我而言,我必须处理具有不同索引的数据帧。

顺便说一句,我最初考虑的是对当前drop_duplicates()方法的扩展,但现在我意识到使用集合论属性的第二种方法在一般情况下会更有用。不过,这两种方法都解决了我目前的问题。

谢谢!

4

12 回答 12

61

有点复杂,但如果你想完全忽略索引数据。将数据帧的内容转换为包含列的元组集:

ds1 = set(map(tuple, df1.values))
ds2 = set(map(tuple, df2.values))

此步骤也将删除数据框中的任何重复项(忽略索引)

set([(1, 2), (3, 4), (2, 3)])   # ds1

然后可以使用 set 方法查找任何内容。例如寻找差异:

ds1.difference(ds2)

给出:set([(1, 2), (3, 4)])

如果需要,可以将其带回数据框。注意必须将集合转换为列表 1,因为集合不能用于构造数据框:

pd.DataFrame(list(ds1.difference(ds2)))
于 2013-08-12T10:43:19.257 回答
49

这是另一个保留索引并且不需要两个数据帧中的索引相同的答案。(编辑:事先确保 df2 中没有重复项)

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

它很快,结果是

   col1  col2
0     4     6
2     5     5
于 2016-10-24T02:11:45.567 回答
33
from pandas import  DataFrame

df1 = DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
df2 = DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})


print(df2[~df2.isin(df1).all(1)])
print(df2[(df2!=df1)].dropna(how='all'))
print(df2[~(df2==df1)].dropna(how='all'))
于 2014-12-20T10:15:25.163 回答
4

按要映射的对象的列应用(df2);查找不在集合中的行(isin就像集合运算符)

In [32]: df2.apply(lambda x: df2.loc[~x.isin(df1[x.name]),x.name])
Out[32]: 
   col1  col2
0     4     6
2     5     5

同样的事情,但包括 df1 中的所有值,但仍包括 df2 中的每列

In [33]: df2.apply(lambda x: df2.loc[~x.isin(df1.values.ravel()),x.name])
Out[33]: 
   col1  col2
0   NaN     6
2     5     5

第二个例子

In [34]: g = pd.DataFrame({'x': [1.2,1.5,1.3], 'y': [4,4,4]})

In [35]: g.columns=df1.columns

In [36]: g
Out[36]: 
   col1  col2
0   1.2     4
1   1.5     4
2   1.3     4

In [32]: g.apply(lambda x: g.loc[~x.isin(df1[x.name]),x.name])
Out[32]: 
   col1  col2
0   1.2   NaN
1   1.5   NaN
2   1.3   NaN

请注意,在 0.13 中,将有一个isin帧级别的运算符,因此类似:df2.isin(df1)应该是可能的

于 2013-08-12T13:00:10.303 回答
4

有3种方法有效,但其中两种有一些缺陷。

方法一(哈希法):

它适用于我测试的所有案例。

df1.loc[:, "hash"] = df1.apply(lambda x: hash(tuple(x)), axis = 1)
df2.loc[:, "hash"] = df2.apply(lambda x: hash(tuple(x)), axis = 1)
df1 = df1.loc[~df1["hash"].isin(df2["hash"]), :]

方法2(字典方法):

如果 DataFrames 包含日期时间列,它将失败。

df1 = df1.loc[~df1.isin(df2.to_dict(orient="list")).all(axis=1), :]

方法3(MultiIndex方法):

我遇到了在具有 None 或 NaN 的列上失败的情况。

df1 = df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)
于 2017-08-10T18:10:20.990 回答
3

通过合并获取交集的索引,然后删除它们:

>>> df_all = pd.DataFrame(np.arange(8).reshape((4,2)), columns=['A','B']); df_all
   A  B
0  0  1
1  2  3
2  4  5
3  6  7
>>> df_completed = df_all.iloc[::2]; df_completed
   A  B
0  0  1
2  4  5
>>> merged = pd.merge(df_all.reset_index(), df_completed); merged
   index  A  B
0      0  0  1
1      2  4  5
>>> df_pending = df_all.drop(merged['index']); df_pending
   A  B
1  2  3
3  6  7
于 2016-08-20T18:23:06.853 回答
2

编辑:您现在可以直接从 pandas 0.24.0 开始的数据帧制作 MultiIndex 对象,这大大简化了这个答案的语法

df1mi = pd.MultiIndex.from_frame(df1)
df2mi = pd.MultiIndex.from_frame(df2)
dfdiff = df2mi.difference(df1mi).to_frame().reset_index(drop=True)

原始答案

Pandas MultiIndex 对象具有作为方法实现的快速集合操作,因此您可以将 DataFrame 转换为 MultiIndex,使用该difference()方法,然后将结果转换回 DataFrame。这个解决方案应该比这里给出的解决方案快得多(从我的简短测试来看大约 100 倍或更多),并且它不依赖于原始帧的行索引。正如 Piotr 在他的回答中提到的那样,这将因空值而失败,因为 np.nan != np.nan。df2 中具有空值的任何行将始终出现在差异中。此外,两个 DataFrame 的列顺序应该相同。

df1mi = pd.MultiIndex.from_arrays(df1.values.transpose(), names=df1.columns)
df2mi = pd.MultiIndex.from_arrays(df2.values.transpose(), names=df2.columns)
dfdiff = df2mi.difference(df1mi).to_frame().reset_index(drop=True)
于 2018-08-06T17:36:42.770 回答
2

假设:

  1. df1 和 df2 具有相同的列
  2. 这是一个集合操作,因此忽略重复项
  3. 集合不是很大,所以你不用担心内存
union = pd.concat([df1,df2])
sym_diff = union[~union.duplicated(keep=False)]
union_of_df1_and_sym_diff = pd.concat([df1, sym_diff])
diff = union_of_df1_and_sym_diff[union_of_df1_and_sym_diff.duplicated()]
于 2017-11-17T23:32:19.273 回答
2

Numpy 的 setdiff1d 可以工作并且可能更快。

对于每一列: np.setdiff1(df1.col1.values, df2.col1.values)

所以像:

setdf = pd.DataFrame({
    col: np.setdiff1d(getattr(df1, col).values, getattr(df2, col).values)
    for col in df1.columns
})

numpy.setdiff1d 文档

于 2020-01-22T10:21:51.617 回答
1

我不确定如何pd.concat()隐式连接重叠的列,但我不得不对@radream 的答案进行一些调整。

从概念上讲,多列上的集合差异(对称)是集合并集(外连接)减去集合交集(或内连接):

df1 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
df2 = pd.DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})
o = pd.merge(df1, df2, how='outer')
i = pd.merge(df1, df2)
set_diff = pd.concat([o, i]).drop_duplicates(keep=False)

这产生:

   col1  col2
0     1     2
2     3     4
3     4     6
4     5     5
于 2017-08-07T20:49:34.703 回答
1

在 Pandas 1.1.0 中,您可以计算唯一行value_counts并找出计数之间的差异:

df1 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
df2 = pd.DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})

diff = df2.value_counts().sub(df1.value_counts(), fill_value=0)

结果:

col1  col2
1     2      -1.0
2     3       0.0
3     4      -1.0
4     6       1.0
5     5       1.0
dtype: float64

获得正数:

diff[diff > 0].reset_index(name='counts')


   col1  col2  counts
0     4     6     1.0
1     5     5     1.0
于 2021-09-20T08:47:37.423 回答
0

即使您在两个数据框中都有多个列,这也应该有效。但请确保两个数据框的列名完全相同。

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

使用多个列,您还可以使用:

col_names=['col_1','col_2']
set_difference = pd.concat([df2[col_names], df1[col_names], 
df1[col_names]]).drop_duplicates(keep=False)
于 2019-06-26T10:57:11.617 回答