74

假设我有一个数组

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

我如何(有效地,Pythonically)找到哪些元素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.8GHz Xeon 上):

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

最快的是排序,1.1s。可疑xor1d的以 2.6s 位居第二,其次是 masking 和 PandasSeries.duplicated的 3.1s、bincount5.6sin1d和 senderle 的setdiff1d均为 7.3s。Steven 的Counter速度稍慢一点,为 10.5 秒;紧随其后的是 Burhan 的Counter.most_common110 秒和 DSM 的Counter360 秒减法。

我将使用排序来提高性能,但我接受 Steven 的回答,因为性能是可以接受的,而且感觉更清晰、更 Pythonic。

编辑:发现 Pandas 解决方案。如果 Pandas 可用,它就很清楚并且性能良好。

4

9 回答 9

67

从 numpy 版本 1.9.0 开始,np.unique有一个参数return_counts可以大大简化您的任务:

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

这类似于 using Counter,除了你得到一对数组而不是映射。我很想知道它们相对于彼此的表现如何。

可能值得一提的是,尽管np.unique由于它的 numpyness 在实践中相当快,但它的算法复杂性比Counter解决方案更差。np.unique是基于排序的,所以在O(n log n)时间上渐近运行。Counter是基于哈希的,因此具有O(n)复杂性。除了最大的数据集之外,这对任何东西都没有多大关系。

于 2018-07-12T05:16:55.327 回答
35

我认为这是在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条件中使用无下标应该更快。

于 2012-07-17T18:25:45.693 回答
12

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

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

[发布不是因为它有效——不是——而是因为我认为你可以减去Counter实例很可爱。]

于 2012-07-17T20:10:22.863 回答
7

对于 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]
于 2012-07-17T18:02:32.603 回答
5

这是另一种使用集合操作的方法,我认为它比您提供的方法更简单:

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

我想您是在要求numpy-only 解决方案,因为如果不是这种情况,那么仅使用 a 就很难争论Counter。我认为您应该明确说明该要求。

于 2012-07-17T18:32:49.790 回答
5

如果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不是由小整数组成,我会使用这种方法。

于 2012-07-17T18:34:33.527 回答
4

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

a = np.array([1, 2, 2, 3, 4, 5, 5, 6])
rep_el = a[np.diff(a) == 0]
于 2015-06-15T04:12:02.757 回答
3

我正在为这个 3 年的问题添加我的解决方案,因为除了 numpy 之外,没有任何解决方案适合我想要或使用的库。此方法查找重复项的索引和不同重复项集的值。

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]
于 2015-06-11T21:04:14.557 回答
2
>>> 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] ] 
于 2019-04-22T13:56:17.140 回答