确定数组中的重复值

Posted

技术标签:

【中文标题】确定数组中的重复值【英文标题】:Determining duplicate values in an array 【发布时间】:2012-07-16 16:53:26 【问题描述】:

假设我有一个数组

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.8GHz Xeon 上):

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

最快的是排序,1.1s。可疑的 xor1d 以 2.6s 排名第二,其次是 masking 和 Pandas Series.duplicated 在 3.1s,bincount 在 5.6s,in1d 和 senderle 的 setdiff1d 均在 7.3s。 Steven 的Counter 只是慢了一点,10.5s;紧随其后的是 Burhan 在 110 秒时的 Counter.most_common 和 DSM 在 360 秒时的 Counter 减法。

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

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

【问题讨论】:

您能解释一下为什么排序解决方案有效吗?我试过了,但由于某种原因,我真的不明白。 @Markus 如果对数组进行排序,则任何重复值都是相邻的。然后,您使用布尔掩码仅获取与前一项相同的项。 不应该是s[:-1][ s[1:] == s[:-1] ]吗?我得到一个 IndexError 否则,布尔掩码比 s-array 短一个元素.... @snake_charmer 我认为早期版本的 numpy 在这方面更加宽容。我会解决的,谢谢。 pandas 似乎提升了一些底层方法的性能。在我的机器上,pandas 只比排序方法慢 29%。 Mad Physicist 提出的方法比排序慢 17%。 【参考方案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 应该会更快。

【讨论】:

注意:Counter(a).items() 必须在python 3中使用【参考方案2】:

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

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

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

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

【讨论】:

【参考方案3】:
>>> 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] ] 

【讨论】:

【参考方案4】:

如果数组是一个排序的 numpy 数组,那么就这样做:

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

【讨论】:

a[1:][np.diff(a) == 0],不是吗?【参考方案5】:

我将我的解决方案添加到这个 3 年前的问题中,因为除了 numpy 之外,没有一个解决方案适合我想要的或使用的库。此方法查找重复项的索引和 distinct 组重复项的值。

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]

【讨论】:

三年后,您也可以为此使用 return_counts 参数到 unique。看我的回答。【参考方案6】:

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

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

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

【讨论】:

不重新计算集更高效:c = Counter(a); result = (c - Counter(c.keys())).keys()【参考方案7】:

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

【讨论】:

【参考方案8】:

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

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

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

【讨论】:

我认为这种方法的一个缺点是 3 重复了,而 1 没有重复。以一种或另一种方式拥有它会很好。 (这与其说是对您的回答的批评,不如说是对 OP 的原始方法的批评。) @StevenRumbalski,是的,我明白你的意思。我的感觉是,如果真正需要的是一个面具而不是一个项目列表,那么重复的3 是有意义的;如果需要的是项目列表,那么我同意没有重复的项目更好。 我不反对使用Counter,但我担心效率和兼容性。【参考方案9】:

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

【讨论】:

x[0] > 1 不应该是 x[1] > 1 吗?后面的 x 代表频率。

以上是关于确定数组中的重复值的主要内容,如果未能解决你的问题,请参考以下文章

确定一个值是不是在 Java 中的一组值中的最快方法是啥?

字典值中的排序列表[重复]

js去除数组中的重复项

jquery值中的通配符[重复]

检索数组列表中的最后一个对象? Java [重复]

根据值中的第一个字符将数组分成多个数组