高效的2d cumsum

时间:2015-07-28 12:52:13

标签: python arrays numpy scipy cumsum

说我有这样的数组

>>> a = np.arange(1,8).reshape((1,-1))
>>> a
array([[1, 2, 3, 4, 5, 6, 7]])

我希望为a中的每个项目创建一个"以及后续4个项目"的cumsum。也就是说,我的预期输出是

1,       2,      3, 4, 5, 6, 7, 8
1+2,     2+3,     ...
1+2+3    2+3+4    ...
1+2+3+4  2+3+4+5  ...

即。包含

的矩阵
1, 2, 3, 4, 5, 0, 0, 0
3, 5, 7, 9, 11,0, 0, 0
6, 9, 12,15,18,0, 0, 0
10,14,18,21,26,0, 0, 0

由于无法对最后3个项目正确执行cumsum操作,因此我预计会有0。我知道如何做一个单一的cumsum。实际上,数组是

a[:4].cumsum().reshape((-1,1)); a[1:5].cumsum().reshape((-1,1))...

水平堆放。但是,我不知道如何以有效的方式做到这一点。这样做的好的矢量化numpy方式是什么?我也开放scipy个套餐,只要它们在效率或可读性方面占据numpy

3 个答案:

答案 0 :(得分:1)

一种可能的方法是使用滚动窗口方法与cumsum()结合使用。

例如:

from numpy.lib.stride_tricks import as_strided

a = np.arange(1, 9) # the starting array
slice_length = 4

然后你可以写:

arr = as_strided(a, (slice_length, len(a)), (a.strides[0], a.strides[0])).cumsum(axis=0)

这可以帮助您完成大部分工作,但要填写剩余的0值,您可以使用切片和分配来获得所需的输出:

arr[:, (1-slice_length):] = 0

然后你有阵列:

>>> arr
array([[ 1,  2,  3,  4,  5,  0,  0,  0],
       [ 3,  5,  7,  9, 11,  0,  0,  0],
       [ 6,  9, 12, 15, 18,  0,  0,  0],
       [10, 14, 18, 22, 26,  0,  0,  0]])

我不知道是否有任何方法可以在NumPy中使用一个矢量化方法生成完全正确的输出(即没有切片)。 (accumulateat,有点像reduceat,可能是添加到NumPy的ufuncs中有趣的事情......)

答案 1 :(得分:1)

您可以使用名为summed area table的简单变体(也称为图像处理应用程序中的积分图像)有效地进行计算。首先,您计算并存储您的总面积表,第一行的完整集合,前面添加了0

a = np.arange(1, 8)
cs = np.concatenate(([0], np.cumsum(a)))

现在,您可以创建下一个n项目的每个" cumsum"作为cs[:n] - cs[:-n]

>>> for n in range(1, 5):
...     print n, '-->', (cs[n:] - cs[:-n])[:4]
...
1 --> [1 2 3 4]
2 --> [3 5 7 9]
3 --> [ 6  9 12 15]
4 --> [10 14 18 22]

您需要按照您想要的形状正确排列它们,但是一旦完成原始计算,您就可以使用单个减法计算输出的每个项目,这个效果大致相同。

答案 2 :(得分:0)

你可以这样使用broadcasting -

In [53]: a
Out[53]: array([ 4, 13,  4, 18,  1,  2, 11, 15])

In [54]: WSZ = 4 # Window size

In [55]: idx = np.arange(WSZ)[:,None] + np.arange(a.size-WSZ+1) # Broadcasted indices

In [56]: a[idx].cumsum(axis=0) # Index into "a" & perform cumsum along axis-0
Out[56]: 
array([[ 4, 13,  4, 18,  1],
       [17, 17, 22, 19,  3],
       [21, 35, 23, 21, 14],
       [39, 36, 25, 32, 29]], dtype=int32)

如果需要,用零填充 -

In [57]: np.lib.pad(a[idx].cumsum(0),((0,0),(0,WSZ-1)),'constant',constant_values=0)
Out[57]: 
array([[ 4, 13,  4, 18,  1,  0,  0,  0],
       [17, 17, 22, 19,  3,  0,  0,  0],
       [21, 35, 23, 21, 14,  0,  0,  0],
       [39, 36, 25, 32, 29,  0,  0,  0]], dtype=int32)