2015-12-30 6 views
7

Chcę napisać funkcję, która pobiera spłaszczoną tablicę jako dane wejściowe i zwraca tablicę o równej długości, zawierającą sumy poprzednich n elementów z tablicy wejściowej, z początkowymi elementami zestawu wyjściowego ustawionymi na NaN.Python/numpy: Najbardziej efektywny sposób na sumowanie n elementów tablicy, tak aby każdy element wyjściowy był sumą poprzednich n elementów wejściowych?

Na przykład, jeśli tablica ma dziesięć elements = [2, 4, 3, 7, 6, 1, 9, 4, 6, 5] i n = 3, wówczas wynikowa tablica powinna być [NaN, NaN, 9, 14, 16, 14, 16, 14, 19, 15].

Jednym ze sposobów mam wymyślić, aby to zrobić:

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 

Czy jest lepiej/bardziej efektywne/więcej „pythonowy” sposób to zrobić?

Z góry dziękuję za pomoc.

+0

możliwy duplikat dzisiejszego postu: http://stackoverflow.com/questions/34524808/how-to-apply-a-function-to-each-element-of-an-array-when-the-result-is- zależny/34527124 # 34527124 – Netwave

+0

Co z * dokładnością *?Spróbuj [1e20,1, -1e20, -1] i n = 3 –

+4

@ DanielSanchez To pytanie dotyczy zupełnie innego rodzaju obliczeń cyklicznych. Ta równa się przeprowadzeniu splotu 1D. –

Odpowiedz

9

Można skorzystać z np.cumsum i wziąć różnicę ED tablicy cumsum i przesunięty wersję nim:

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, jest to bardziej numpyish sposobem, aby to zrobić, przede wszystkim dlatego, unika pętli.


Timings

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

Jesteś w zasadzie wykonywania 1D convolution tam, więc można użyć np.convolve, jak tak -

# 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)) 

Sample Run -

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.]) 

Dla splotu 1D można również użyć Scipy's implementation. Środowiska wykonawcze z wersją Scipy wydawały się lepsze dla dużego rozmiaru okna, jak również testowane poniżej testy środowiska wykonawczego spróbowałyby zbadać. Wersja scipy uzyskania vals byłoby -

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

NaNs operacja wyściółka może być zastąpiony przez np.hstack: np.hstack(([np.nan]*(n-1),vals)) dla lepszej wydajności.


Runtime testy -

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

innych odpowiedzi są tu prawdopodobnie bliżej do czego szukasz pod względem szybkości i pamięci, ale dla kompletności można też użyć do budowania listy ze zrozumieniem macierzy:

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)]) 

powraca:

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