2015-12-30 162 views
7

我想寫一個函數,它將一個扁平數組作爲輸入,並返回一個包含輸入數組中前n個元素的和的相等長度的數組,初始的n - 1輸出數組的元素設置爲NaNPython/numpy:總結一個數組的n個元素的最有效方法,以便每個輸出元素是前n個輸入元素的總和?

例如,如果陣列有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化」的方式來做到這一點?

在此先感謝您的幫助。

+0

今天發帖的可能重複:http://stackoverflow.com/questions/34524808/how-to-apply-a-function-to-each-element-of-an-array-when-the-ultult-is-依賴/ 34527124#34527124 – Netwave

+0

*準確性*?嘗試[1e20,1,-1e20,-1]和n = 3 –

+4

@DanielSanchez這個問題是關於一種非常不同類型的經常性計算。這相當於執行一維卷積。 –

回答

9

您可以使用​​,並採取cumsum版陣列的差異,它的移動版本:

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 
7

你基本上執行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版本的運行時對於大窗口大小似乎更好,因爲下面列出的運行時測試也會嘗試進行調查。爲更好的性能np.hstack(([np.nan]*(n-1),vals)): - 該SciPy的版本越來越vals

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

NaNs填充操作可能被np.hstack取代。


運行測試 -

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 
4

其他的答案這裏有可能更接近你的速度和內存方面尋找,但出於完整性你也可以使用列表理解建設什麼你的數組:

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.]) 
相關問題