我有一个包含对象ID的数组traced_descIDs
,我想确定哪些项在此数组中不唯一。然后,对于每个唯一的重复(仔细)ID,我需要确定traced_descIDs
的哪些索引与之关联。
例如,如果我们在这里使用traced_descIDs,我希望发生以下过程:
traced_descIDs = [1, 345, 23, 345, 90, 1]
dupIds = [1, 345]
dupInds = [[0,5],[1,3]]
我目前正在查找哪些对象有多个条目:
mentions = np.array([len(np.argwhere( traced_descIDs == i)) for i in traced_descIDs])
dupMask = (mentions > 1)
然而,这需要很长时间,因为len( traced_descIDs )
大约是150,000。有没有更快的方法来实现相同的结果?
任何帮助非常感谢。欢呼声。
答案 0 :(得分:7)
虽然字典是O(n),但Python对象的开销有时会使得使用numpy的函数更加方便,这些函数使用排序并且是O(n * log n)。在您的情况下,起点是:
a = [1, 345, 23, 345, 90, 1]
unq, unq_idx, unq_cnt = np.unique(a, return_inverse=True, return_counts=True)
如果你使用的是早于1.9的numpy版本,那么最后一行必须是:
unq, unq_idx = np.unique(a, return_inverse=True)
unq_cnt = np.bincount(unq_idx)
我们创建的三个数组的内容是:
>>> unq
array([ 1, 23, 90, 345])
>>> unq_idx
array([0, 3, 1, 3, 2, 0])
>>> unq_cnt
array([2, 1, 1, 2])
获取重复的项目:
cnt_mask = unq_cnt > 1
dup_ids = unq[cnt_mask]
>>> dup_ids
array([ 1, 345])
获取索引更为复杂,但非常简单:
cnt_idx, = np.nonzero(cnt_mask)
idx_mask = np.in1d(unq_idx, cnt_idx)
idx_idx, = np.nonzero(idx_mask)
srt_idx = np.argsort(unq_idx[idx_mask])
dup_idx = np.split(idx_idx[srt_idx], np.cumsum(unq_cnt[cnt_mask])[:-1])
>>> dup_idx
[array([0, 5]), array([1, 3])]
答案 1 :(得分:5)
scipy.stats.itemfreq
会给出每个项目的频率:
>>> xs = np.array([1, 345, 23, 345, 90, 1])
>>> ifreq = sp.stats.itemfreq(xs)
>>> ifreq
array([[ 1, 2],
[ 23, 1],
[ 90, 1],
[345, 2]])
>>> [(xs == w).nonzero()[0] for w in ifreq[ifreq[:,1] > 1, 0]]
[array([0, 5]), array([1, 3])]
答案 2 :(得分:2)
您当前的方法是O(N**2)
,请使用字典在O(N)
时间内执行此操作:
>>> from collections import defaultdict
>>> traced_descIDs = [1, 345, 23, 345, 90, 1]
>>> d = defaultdict(list)
>>> for i, x in enumerate(traced_descIDs):
... d[x].append(i)
...
>>> for k, v in d.items():
... if len(v) == 1:
... del d[k]
...
>>> d
defaultdict(<type 'list'>, {1: [0, 5], 345: [1, 3]})
获取物品和指数:
>>> from itertools import izip
>>> dupIds, dupInds = izip(*d.iteritems())
>>> dupIds, dupInds
((1, 345), ([0, 5], [1, 3]))
请注意,如果您想保留dupIds
中项目的顺序,请使用collections.OrderedDict
和dict.setdefault()
方法。
答案 3 :(得分:1)
td = np.array(traced_descIDs)
si = np.argsort(td)
td[si][np.append(False, np.diff(td[si]) == 0)]
这会给你:
array([ 1, 345])
我还没有弄清楚第二部分,但也许这对你来说足够灵感,或者我可能会回到它。 :)
答案 4 :(得分:0)
Jaime提出的具有相同矢量化效率的解决方案嵌入numpy_indexed包中(免责声明:我是其作者):
import numpy_indexed as npi
print(npi.group_by(traced_descIDs, np.arange(len(traced_descIDs))))
这让我们大部分都在那里;但是如果我们还希望过滤掉单例组,同时避免任何python循环并保持完全向量化,我们可以降低一点,并且:
g = npi.group_by(traced_descIDs)
unique = g.unique
idx = g.split_array_as_list(np.arange(len(traced_descIDs)))
duplicates = unique[g.count>1]
idx_duplicates = np.asarray(idx)[g.count>1]
print(duplicates, idx_duplicates)
答案 5 :(得分:0)
np.unqiue
for Ndims 我在ndArray上也遇到了类似的问题,我想在其中查找重复的行。
x = np.arange(60).reshape(5,4,3)
x[1] = x[0]
0和1应该在轴0上重复。我使用了np.unique
并返回了所有选项。然后使用Jaime的方法查找重复项。
_,i,_,c = np.unique(x,1,1,1,axis=0)
x_dup = x[i[1<c]]
为清楚起见,我不必要使用return_inverse
。结果如下:
>>> print(x_dupilates)
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]]