Python/numpy:最有效的方法是对数组的n个元素求和,使每个输出元素都是前n个输入元素的和?

mlmc2os5  于 12个月前  发布在  Python
关注(0)|答案(3)|浏览(93)

我想写一个函数,它以一个展平的数组作为输入,并返回一个等长的数组,其中包含输入数组中前n个元素的和,输出数组的初始n - 1元素设置为NaN
例如,如果数组有10个elements = [2, 4, 3, 7, 6, 1, 9, 4, 6, 5]n = 3,那么结果数组应该是[NaN, NaN, 9, 14, 16, 14, 16, 14, 19, 15]
我想到的一个方法是:

def sum_n_values(flat_array, n): 
    
    sums = np.full(flat_array.shape, np.NaN)
    for i in range(n - 1, flat_array.shape[0]):
        sums[i] = np.sum(flat_array[i - n + 1:i + 1])
    return sums

有没有更好/更有效/更“Python”的方法来做到这一点?

sf6xfgos

sf6xfgos1#

你可以使用np.cumsum,并取cumsum ed数组和它的移位版本的差:

n = 3
arr = np.array([2, 4, 3, 7, 6, 1, 9, 4, 6, 5])
sum_arr = arr.cumsum()
shifted_sum_arr = np.concatenate([[np.NaN]*(n-1), [0],  sum_arr[:-n]])
sum_arr
=> array([ 2,  6,  9, 16, 22, 23, 32, 36, 42, 47])
shifted_sum_arr
=> array([ nan,  nan,   0.,   2.,   6.,   9.,  16.,  22.,  23.,  32.])
sum_arr - shifted_sum_arr
=> array([ nan,  nan,   9.,  14.,  16.,  14.,  16.,  14.,  19.,  15.])

IMO,这是一个更 numpyish 的方式来做到这一点,主要是因为它避免了循环。

时间

def cumsum_app(flat_array, n):
    sum_arr = flat_array.cumsum()
    shifted_sum_arr = np.concatenate([[np.NaN]*(n-1), [0],  sum_arr[:-n]])
    return sum_arr - shifted_sum_arr

flat_array = np.random.randint(0,9,(100000))
%timeit cumsum_app(flat_array,10)
1000 loops, best of 3: 985 us per loop
%timeit cumsum_app(flat_array,100)
1000 loops, best of 3: 963 us per loop
aelbi1ox

aelbi1ox2#

你基本上是在执行1D convolution,所以你可以使用np.convolve,就像这样-

# Get the valid sliding summations with 1D convolution
vals = np.convolve(flat_array,np.ones(n),mode='valid')

# Pad with NaNs at the start if needed  
out = np.pad(vals,(n-1,0),'constant',constant_values=(np.nan))

样品运行-

In [110]: flat_array
Out[110]: array([2, 4, 3, 7, 6, 1, 9, 4, 6, 5])

In [111]: n = 3

In [112]: vals = np.convolve(flat_array,np.ones(n),mode='valid')
     ...: out = np.pad(vals,(n-1,0),'constant',constant_values=(np.nan))
     ...: 

In [113]: vals
Out[113]: array([  9.,  14.,  16.,  14.,  16.,  14.,  19.,  15.])

In [114]: out
Out[114]: array([ nan,  nan,   9.,  14.,  16.,  14.,  16.,  14.,  19.,  15.])

对于1D卷积,也可以使用Scipy's implementation。Scipy版本的运行时似乎更适合大的窗口大小,因为接下来列出的运行时测试也将尝试调查。获取vals的Scipy版本是-

from scipy import signal
vals = signal.convolve(flat_array,np.ones(n),mode='valid')

NaNs填充操作可以被np.hstack替换:np.hstack(([np.nan]*(n-1),vals))以获得更好的性能。
测试-

In [238]: def original_app(flat_array,n):
     ...:     sums = np.full(flat_array.shape, np.NaN)
     ...:     for i in range(n - 1, flat_array.shape[0]):
     ...:         sums[i] = np.sum(flat_array[i - n + 1:i + 1])
     ...:     return sums
     ...: 
     ...: def vectorized_app1(flat_array,n):
     ...:     vals = np.convolve(flat_array,np.ones(n),mode='valid')
     ...:     return np.hstack(([np.nan]*(n-1),vals))
     ...: 
     ...: def vectorized_app2(flat_array,n):
     ...:     vals = signal.convolve(flat_array,np.ones(3),mode='valid')
     ...:     return np.hstack(([np.nan]*(n-1),vals))
     ...: 

In [239]: flat_array = np.random.randint(0,9,(100000))

In [240]: %timeit original_app(flat_array,10)
1 loops, best of 3: 833 ms per loop

In [241]: %timeit vectorized_app1(flat_array,10)
1000 loops, best of 3: 1.96 ms per loop

In [242]: %timeit vectorized_app2(flat_array,10)
100 loops, best of 3: 13.1 ms per loop

In [243]: %timeit original_app(flat_array,100)
1 loops, best of 3: 836 ms per loop

In [244]: %timeit vectorized_app1(flat_array,100)
100 loops, best of 3: 16.5 ms per loop

In [245]: %timeit vectorized_app2(flat_array,100)
100 loops, best of 3: 13.1 ms per loop
5w9g7ksd

5w9g7ksd3#

这里的其他答案可能更接近你在速度和内存方面所寻求的,但为了完整起见,你也可以使用列表解析来构建你的数组:

a = np.array([2, 4, 3, 7, 6, 1, 9, 4, 6, 5])
N, n = a.shape[0], 3
np.array([np.NaN]*(n-1) + [np.sum(a[j:j+n]) for j in range(N-n+1)])

返回:

array([ nan,  nan,   9.,  14.,  16.,  14.,  16.,  14.,  19.,  15.])

相关问题