在数组中确定重复值

90

假设我有一个数组

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]

Set 操作

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]]

Pandas

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

我有什么遗漏的吗?我不一定要求使用仅限于numpy的解决方案,但它必须能够处理numpy数据类型并且在中等大小的数据集(最多1000万)上具有高效性。


结论

在一个1000万大小的数据集上进行测试(在2.8GHz Xeon上):

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

最快的方法是排序,只需1.1秒。第二是可疑的xor1d,需要2.6秒,接下来是掩码和Pandas的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的答案,因为它看起来更加清晰和符合Pythonic风格。

编辑:发现了Pandas的解决方案。如果有Pandas可用,则该解决方案效果明显且性能良好。


2
你能解释一下为什么这个排序解决方案有效吗?我尝试了一下,但出于某种原因,我真的不太明白。 - Markus
2
@Markus 如果你对一个数组进行排序,任何重复的值都会相邻。然后你可以使用布尔掩码来仅选择与前一个项目相等的项。 - ecatmur
1
难道不应该是s[:-1][ s[1:] == s[:-1] ]吗?否则我会得到一个IndexError,因为布尔掩码比s数组少一个元素... - snake_charmer
@snake_charmer 我认为早期版本的numpy在这方面更加宽容。我会修复它,谢谢。 - ecatmur
pandas似乎已经改进了一些底层方法的性能。在我的机器上,pandas仅比排序方法慢29%。Mad Physicist提出的方法比排序慢17%。 - JE_Muc
FYI:我刚试过对一个普通列表进行排序,但是无效。然而,如果使用已排序的 numpy array,则可以起作用。 - Godrebh
11个回答

1

使用计数器的减法方法可以避免创建第二个计数器,就像DSM's answer中所示,并且要获取仅为正数的计数(即:重复项),请对结果计数器使用一元+运算符

a = [1, 2, 1, 3, 3, 3, 0]

c = Counter(a)
c.subtract(d.keys())
dupes = (+c).keys()

在我的测试中,我发现这是表现最佳的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接