确定数组中的重复值
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 代表频率。以上是关于确定数组中的重复值的主要内容,如果未能解决你的问题,请参考以下文章