2015-12-30 5 views
7

フラットな配列を入力として受け取り、出力配列の最初のn - 1要素がNaNに設定された、入力配列の前のn要素の合計を含む等しい長さの配列を返します。Python/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-result-is-依存/ 34527124#34527124 – Netwave

+0

*精度*はどうですか?[1e20,1、-1e20、-1]とn = 3を試してください。 –

+4

@DanielSanchezこの質問は非常に異なるタイプの反復計算に関するものです。これは1Dコンボリューションを実行することになります。 –

答えて

9

あなたは​​の使用を行い、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 
7

あなたがそうのように、np.convolveを使用することができますので、あなたは基本的に、そこに1D convolutionを実行している -

# 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)): - valsは次のようになり取得するためのscipyのダウンロード版

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.]) 
関連する問題