确定数组中的重复值

假设我有一个数组

a = np.array([1, 2, 1, 3, 3, 3, 0])

我如何(高效地,Python 地)找到 a的哪些元素是重复的(即,非唯一值) ?在这种情况下,如果有效的话,结果可能是 array([1, 3, 3])或者可能是 array([1, 3])

我想出了一些看起来有效的方法:

掩饰

m = np.zeros_like(a, dtype=bool)
m[np.unique(a, return_index=True)[1]] = True
a[~m]

开始行动

a[~np.in1d(np.arange(len(a)), np.unique(a, return_index=True)[1], assume_unique=True)]

这个很可爱,但可能是非法的(因为 a实际上并不是唯一的) :

np.setxor1d(a, np.unique(a), assume_unique=True)

直方图

u, i = np.unique(a, return_inverse=True)
u[np.bincount(i) > 1]

分类

s = np.sort(a, axis=None)
s[:-1][s[1:] == s[:-1]]

熊猫

s = pd.Series(a)
s[s.duplicated()]

我错过了什么吗?我不一定要寻找一个只使用 numpy 的解决方案,但它必须能够处理 numpy 数据类型,并且在中等规模的数据集(最大可达1000万)上有效。


结论

使用1000万大小的数据集进行测试(在2.8 GHz 至强上) :

a = np.random.randint(10**7, size=10**7)

最快的是排序,1.1秒。可疑的 xor1d在2.6秒处于第二位,其次是掩蔽和熊猫 Series.duplicated在3.1秒处,bincount在5.6秒处,in1d和 senderle 的 setdiff1d都在7.3秒处。 Steven 的 Counter只是稍微慢一点,在10.5秒处; 紧随其后的是 Burhan 的 Counter.most_common在110秒处,DSM 的 Counter减法在360秒处。

我将使用性能排序,但是我接受 Steven 的答案,因为性能是可以接受的,而且它的 感觉更清晰,更 Python 化。

编辑: 发现了熊猫的解决方案。如果熊猫是可用的,它的清晰和执行良好。

100850 次浏览

对于 Python 2.7 +

>>> import numpy
>>> from collections import Counter
>>> n = numpy.array([1,1,2,3,3,3,0])
>>> [x[1] for x in Counter(n).most_common() if x[0] > 1]
[3, 1]

我认为这是最明确的外部 numpy完成。如果您关心速度,那么您必须根据 numpy解决方案来计时。

>>> import numpy as np
>>> from collections import Counter
>>> a = np.array([1, 2, 1, 3, 3, 3, 0])
>>> [item for item, count in Counter(a).items() if count > 1]
[1, 3]

注意: 这与 Burhan Khalid 的回答相似,但是在条件下使用 items无需订阅应该会更快。

下面是另一种使用 set 操作的方法,我认为它比你提供的方法要简单一些:

>>> indices = np.setdiff1d(np.arange(len(a)), np.unique(a, return_index=True)[1])
>>> a[indices]
array([1, 3, 3])

我想您要求的是仅使用 numpy的解决方案,因为如果不是这种情况,那么就很难反驳只使用 Counter的说法。我认为你应该明确这个要求。

如果 a是由小整数组成的,你可以直接使用 numpy.bincount:

import numpy as np


a = np.array([3, 2, 2, 0, 4, 3])
counts = np.bincount(a)
print np.where(counts > 1)[0]
# array([2, 3])

这与您的“直方图”方法非常相似,如果 a不是由小整数组成,那么我将使用这种方法。

人们已经提出了 Counter的变体,但是这里有一个没有使用 listcomp:

>>> from collections import Counter
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> (Counter(a) - Counter(set(a))).keys()
[1, 3]

[发表这篇文章并不是因为它高效——它不是——而是因为我觉得可以减去 Counter实例很可爱。]

我将我的解决方案添加到这堆3年的问题,因为没有一个解决方案适合我想要的或使用库除了麻木。这个方法同时查找重复项的索引和 很明显重复项集的值。

import numpy as np


A = np.array([1,2,3,4,4,4,5,6,6,7,8])


# Record the indices where each unique element occurs.
list_of_dup_inds = [np.where(a == A)[0] for a in np.unique(A)]


# Filter out non-duplicates.
list_of_dup_inds = filter(lambda inds: len(inds) > 1, list_of_dup_inds)


for inds in list_of_dup_inds: print inds, A[inds]
# >> [3 4 5] [4 4 4]
# >> [7 8] [6 6]

如果该数组是一个排序的 numpy 数组,那么只需执行以下操作:

a = np.array([1, 2, 2, 3, 4, 5, 5, 6])
rep_el = a[np.diff(a) == 0]

从 numpy 版本1.9.0开始,np.unique有一个参数 return_counts,它极大地简化了你的任务:

u, c = np.unique(a, return_counts=True)
dup = u[c > 1]

这类似于使用 Counter,只不过得到的是一对数组而不是一个映射。我很好奇他们相对于彼此的表现如何。

值得一提的是,尽管 np.unique由于其麻木而在实践中相当快,但它的算法复杂度比 Counter解决方案更差。np.unique是基于排序的,因此在 O(n log n)时间内运行是渐近的。Counter是基于散列的,所以 O(n)也是复杂的。除了最大的数据集之外,这对其他任何东西都不重要。

>>> import numpy as np


>>> a=np.array([1,2,2,2,2,3])


>>> uniques, uniq_idx, counts = np.unique(a,return_index=True,return_counts=True)
>>> duplicates = a[ uniq_idx[counts>=2] ]  # <--- Get duplicates

如果你也想得到孤儿:

>>> orphans = a[ uniq_idx[counts==1] ]

熊猫和笨蛋的结合(使用 value _ count () :

import pandas as pd
import numpy as np


arr=np.array(('a','b','b','c','a'))
pd.Series(arr).value_counts()

产出:

a    2
b    2
c    1