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]更快的方式得到分类指数sidxscaling -

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 
+0

非常感谢您@Divakar。我在你的回复中学到了很多技巧。 在上面的代码中,我应该考虑'np.flatnonzero(pos [1:]!= pos [: - 1])是否是一种在数组中定位唯一元素的替代方法?从我的角度来看,就好像我们拒绝了零导数的位置并保留其余部分。我是否正确理解你将-1附加到它,考虑到这种分类方式使得最后一个元素必然是最重的元素? –

+1

@ user31412'np.flatnonzero(pos [1:]!= pos [: - 1])基本上是通过切片一个切片得到间隔*改变的索引。我们需要这些,因为我们打算得到每个组的最后一个,因为lexsort/argsort会代表每个组/间隔的最大参数。最后-1是需要的,因为切片会错过获取最后一组的最后一个元素。所以,我们手动添加。这给了我们最后一组的argmax,因为sidx会把那个作为最后一个元素。希望这是有道理的。 – Divakar

+0

当然,这对我有意义。我要玩你提供的不同方法。非常感谢。只要我确信我能理解你写的所有内容,我就会马上接受这个答案。 –