Interval对于一个NumPy数组,其间隔由另一个数组定义的argmax
问题描述:
我有一个排序的非唯一数字的一维数组。他们重复的次数是随机的。 它与具有相同大小的权重数组相关联。对于给定的一系列相同的元素,相关的一系列权重可能也可能不具有重复的元素,并且在整个权重阵列中,可能有也可能不会有重复的元素。 E.g:Interval对于一个NumPy数组,其间隔由另一个数组定义的argmax
pos = np.array([3, 3, 7, 7, 9, 9, 9, 10, 10])
weights = np.array([2, 10, 20, 8, 5, 7, 15, 7, 2])
我需要提取的pos
独特元件的阵列,但是其中唯一元件是一个具有最大权重。
我想出了一个工作的解决方案涉及循环:
pos = np.array([3, 3, 7, 7, 9, 9, 9, 10, 10])
weights = np.array([2, 10, 20, 8, 5, 7, 15, 7, 2])
# Get the number of occurences of the elements in pos but throw away the unique array, it's not the one I want.
_, ucounts = np.unique(pos, return_counts=True)
# Initialize the output array.
unique_pos_idx = np.zeros([ucounts.size], dtype=np.uint32)
last = 0
for i in range(ucounts.size):
maxpos = np.argmax(weights[last:last+ucounts[i]])
unique_pos_idx[i] = last + maxpos
last += ucounts[i]
# Result is:
# unique_pos_idx = [1 2 6 7]
,但我不使用太多的Python语言或numpy的(除了使用numpy的阵列),所以我不知道是否有一个更多Pythonesque和/或更高效的解决方案,甚至比以上的Cython版本更多?
感谢
答
这里有一个量化的方法 - 性能
sidx = np.lexsort([weights,pos])
out = sidx[np.r_[np.flatnonzero(pos[1:] != pos[:-1]), -1]]
可能改善(S) -
1]更快的方式得到分类指数sidx
与scaling
-
sidx = (pos*(weights.max()+1) + weights).argsort()
2]在e处的索引第二,可以由具有boolean-indexing
更快,特别是用许多这样的间隔/团体打交道时 -
out = sidx[np.concatenate((pos[1:] != pos[:-1], [True]))]
运行测试
所有的方法:
def org_app(pos, weights):
_, ucounts = np.unique(pos, return_counts=True)
unique_pos_idx = np.zeros([ucounts.size], dtype=np.uint32)
last = 0
for i in range(ucounts.size):
maxpos = np.argmax(weights[last:last+ucounts[i]])
unique_pos_idx[i] = last + maxpos
last += ucounts[i]
return unique_pos_idx
def vec_app(pos, weights):
sidx = np.lexsort([weights,pos])
return sidx[np.r_[np.flatnonzero(pos[1:] != pos[:-1]), -1]]
def vec_app_v2(pos, weights):
sidx = (pos*(weights.max()+1) + weights).argsort()
return sidx[np.concatenate((pos[1:] != pos[:-1], [True]))]
时序和验证 -
对于设置,让我们使用该示例并将其瓦片10000
次小时缩放,因为我们打算创建1000
倍以上的时间间隔。另外,让我们用识别号weights
,使argmax
指数不相同的数字迷惑:
In [155]: # Setup input
...: pos = np.array([3, 3, 7, 7, 9, 9, 9, 10, 10,])
...: pos = (pos + 10*np.arange(10000)[:,None]).ravel()
...: weights = np.random.choice(10*len(pos), size=len(pos), replace=0)
...:
...: print np.allclose(org_app(pos, weights), vec_app(pos, weights))
...: print np.allclose(org_app(pos, weights), vec_app_v2(pos, weights))
...:
True
True
In [156]: %timeit org_app(pos, weights)
...: %timeit vec_app(pos, weights)
...: %timeit vec_app_v2(pos, weights)
...:
10 loops, best of 3: 56.4 ms per loop
100 loops, best of 3: 14.8 ms per loop
1000 loops, best of 3: 1.77 ms per loop
In [157]: 56.4/1.77 # Speedup with vectorized one over loopy
Out[157]: 31.864406779661017
非常感谢您@Divakar。我在你的回复中学到了很多技巧。 在上面的代码中,我应该考虑'np.flatnonzero(pos [1:]!= pos [: - 1])是否是一种在数组中定位唯一元素的替代方法?从我的角度来看,就好像我们拒绝了零导数的位置并保留其余部分。我是否正确理解你将-1附加到它,考虑到这种分类方式使得最后一个元素必然是最重的元素? –
@ user31412'np.flatnonzero(pos [1:]!= pos [: - 1])基本上是通过切片一个切片得到间隔*改变的索引。我们需要这些,因为我们打算得到每个组的最后一个,因为lexsort/argsort会代表每个组/间隔的最大参数。最后-1是需要的,因为切片会错过获取最后一组的最后一个元素。所以,我们手动添加。这给了我们最后一组的argmax,因为sidx会把那个作为最后一个元素。希望这是有道理的。 – Divakar
当然,这对我有意义。我要玩你提供的不同方法。非常感谢。只要我确信我能理解你写的所有内容,我就会马上接受这个答案。 –