在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
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%!