python索引⾃定义_python–使⽤多个⾃定义索引范围构建
numpy数组,⽽不显式循环...
在Numpy中,是否有⼀种pythonic⽅法来创建array3,其中⾃定义范围来⾃array1和array2⽽没有循环?迭代范围的直接解决⽅案有效,但由于我的数组遇到了数百万个项⽬,我正在寻更有效的解决⽅案(也可能是语法糖).
例如,
array1 = np.array([10, 65, 200])
array2 = np.array([14, 70, 204])
array3 = np.concatenate([np.arange(array1[i], array2[i]) for i in
np.arange(0,len(array1))])
print array3
结果:[10,11,12,13,65,66,67,68,69,200,201,202,203].
解决⽅法:
前瞻性⽅法
我将回顾如何解决这个问题.
采取问题中列出的样本.我们有 –
array1 = np.array([10, 65, 200])
array2 = np.array([14, 70, 204])
现在,看看想要的结果 –
result: [10,11,12,13,65,66,67,68,69,200,201,202,203]
让我们计算组长度,因为我们需要那些解释下⼀步的解决⽅案.
In [58]: lens = array2 - array1
In [59]: lens
Out[59]: array([4, 5, 4])
我们的想法是使⽤1的初始化数组,当在整个长度上进⾏累积求和时,可以得到所需的结果.
这个累积总和将是我们解决⽅案的最后⼀步.
为什么1的初始化?好吧,因为我们有⼀个数组以1的步长增加,除了在我们有变化的特定地⽅
对应新的团体进来.
现在,因为cumsum将是最后⼀步,所以它之前的步骤应该给我们⼀些像 –
array([ 10, 1, 1, 1, 52, 1, 1, 1, 1, 131, 1, 1, 1])
正如前⾯所讨论的那样,在特定的地⽅,它的1是[10,52,131].那个10似乎是从array1中的第⼀个元素进来的,但其余的呢?
第⼆个52以65-13(看结果)进⼊,其中13个进⼊了以10开头并且因为长度⽽跑的组
第⼀组4.所以,如果我们做65 – 10 – 4,我们将获得51然后加1以适应边界停⽌,我们将有52,这是
期望的转移价值.同样,我们会得到131.
因此,可以像这样计算那些移位值 –
In [62]: np.diff(array1) - lens[:-1]+1
Out[62]: array([ 52, 131])
接下来,为了获得发⽣这种转变的那些转移位置,我们可以简单地对组长度进⾏累积求和 –In [65]: lens[:-1].cumsum()
Out[65]: array([4, 9])
为了完整性,我们需要为移位值预先附加0,使⽤array1 [0]作为移位值.
因此,我们将逐步展⽰我们的⽅法!
把它们放回去
1]获取每组的长度:
lens = array2 - array1
2]获取发⽣偏移的索引和要放⼊1的初始化数组中的值:
shift_idx = np.hstack((0,lens[:-1].cumsum()))
shift_vals = np.hstack((array1[0],np.diff(array1) - lens[:-1]+1))
3] Setup 1的初始化ID数组,⽤于将这些值插⼊前⾯步骤中列出的那些索引:
id_arr = np.ones(lens.sum(),dtype=array1.dtype)
id_arr[shift_idx] = shift_vals
4]最后对ID数组进⾏累加求和:
output = id_arr.cumsum()
以功能格式列出,我们会 –
def using_ones_cumsum(array1, array2):
lens = array2 - array1
python获取数组长度shift_idx = np.hstack((0,lens[:-1].cumsum()))
shift_vals = np.hstack((array1[0],np.diff(array1) - lens[:-1]+1))
id_arr = np.ones(lens.sum(),dtype=array1.dtype)
id_arr[shift_idx] = shift_vals
return id_arr.cumsum()
它也适⽤于重叠范围!
In [67]: array1 = np.array([10, 11, 200])
...: array2 = np.array([14, 18, 204])
...:
In [68]: using_ones_cumsum(array1, array2)
Out[68]:
array([ 10, 11, 12, 13, 11, 12, 13, 14, 15, 16, 17, 200, 201,
202, 203])
运⾏时测试
让我们来讨论@unutbu's flatnonzero based solution中针对其他⽮量化⽅法的提议⽅法,这种⽅法已经证明⽐循环⽅法好得多 –In [38]: array1, array2 = (np.random.choice(range(1, 11), size=10**4, replace=True)
...: .cumsum().reshape(2, -1, order='F'))
In [39]: %timeit using_flatnonzero(array1, array2)
1000 loops, best of 3: 889 µs per loop
In [40]: %timeit using_ones_cumsum(array1, array2)
1000 loops, best of 3: 235 µs per loop
改进!
现在,代码NumPy不喜欢追加.因此,对于稍微改进的版本,可以避免那些np.hstack调⽤,如下所⽰ –
def get_ranges_arr(starts,ends):
counts = ends - starts
counts_csum = counts.cumsum()
id_arr = np.ones(counts_csum[-1],dtype=int)
id_arr[0] = starts[0]
id_arr[counts_csum[:-1]] = starts[1:] - ends[:-1] + 1
return id_arr.cumsum()
让我们反对我们原来的做法 –
In [151]: array1,array2 = (np.random.choice(range(1, 11),size=10**4, replace=True)\
...: .cumsum().reshape(2, -1, order='F'))
In [152]: %timeit using_ones_cumsum(array1, array2)
1000 loops, best of 3: 276 µs per loop
In [153]: %timeit get_ranges_arr(array1, array2)
10000 loops, best of 3: 193 µs per loop
所以,我们的性能提升了30%!
标签:python,arrays,vectorization,numpy,performance