NumPyを使用して移動平均を計算する方法は?


109

単純にnumpy / scipyで移動平均を計算する関数はないようで、複雑な解につながります

私の質問は2つあります。

  • numpyで移動平均を(正しく)実装する最も簡単な方法は何ですか?
  • これは簡単ではなく、エラーが発生しやすいように思われるので、このケースにバッテリーを含めないことには十分な理由がありますか?

19
畳み込みソリューションは、私にはそれほど複雑ではありません!
2013年

4
移動平均は単なるローパスフィルター(つまり、「ぼかし」)ではありませんか?それがまさに、たたみ込みが意図するものの種類だと確信している...
user541686

@mmgp私は私が間違っていることを望んでいたと思います、または、正当で明白な理由があったと思います。
goncalopp 2013年

3
@wim駄洒落のような意味のあるものだった。しかし、問題が存在するという単なる事実は、numpy.convoluteから移動平均を作成することは簡単ではないことを意味します。
goncalopp 2013年

回答:


164

単純な非加重移動平均が必要な場合は、を使用して簡単に実装できますnp.cumsum。これ 、FFTベースのメソッドよりも高速な場合があります。

EDITコード内のBeanによって検出された1つずれる間違ったインデックス付けを修正しました。編集する

def moving_average(a, n=3) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

>>> a = np.arange(20)
>>> moving_average(a)
array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.])
>>> moving_average(a, n=4)
array([  1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
        10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5])

だから私は答えを推測します:それは実装するのが本当に簡単で、おそらくnumpyはすでに特殊な機能で少し肥大化しています。


10
このコードは正しくありません。たとえば、moving_average([1,2,5,10]、n = 2)は[1.、3.5、8.5]を返します。0から19までの値の移動平均に対する回答者のテストケースでさえ、0、1、2の平均は0.5であると主張しており、正しくありません。どのようにして6つの賛成票を獲得しましたか?
JeremyKun 2013

2
バグチェックをありがとう、それは今うまく機能しているようです。賛成票に関しては、答えの背後にある一般的な考えは、実装の1つずれたエラーよりも重要視されていたと思いますが、誰が知っていますか。
Jaime

2
問題を発見しました。ret[n:] -= ret[:-n]と同じではありませんret[n:] = ret[n:] - ret[:-n]。この回答のコードを修正しました。編集:いいえ、他の誰かが私に打ち負かしました。
Timmmm

7
@Timmmm私がやった、それは確かに問題でした。この回答の背後にある一般的な原則は、画像処理(それらが呼び出す総面積テーブル)で広く使用されているため、問題は実装にある必要がありました。「より効率的になるため」操作をインプレースで実行したことを思い出すので、時期尚早の最適化に噛み付いた良い例です。明るい面では、おそらく間違った答えをより速く生成しました...
Jaime

43
うーん、この「実装が簡単」な機能は実際には間違いを犯しやすく、メモリ効率についての良い議論を促進しているようです。何かが正しく行われたことを知ることを意味するなら、私は膨満感があってうれしいです。
Richard

81

NumPyが特定のドメイン固有の機能を欠いているのは、おそらくCoreチームの規律とNumPyの主要な指令への忠実性が原因です。N次元の配列型と、これらの配列を作成およびインデックス付けするための関数を提供します。多くの基本的な目的のように、これは小さくはなく、NumPyは見事にそれを行います。

(はるかに)大きなSciPyには、ドメイン固有のライブラリ(SciPy開発者によってサブパッケージと呼ばれる)のはるかに大きなコレクションが含まれています -たとえば、数値最適化(optimize)、信号処理(signal)、積分計算(統合)。

私の推測では、あなたが求めている関数は、少なくとも1つのSciPyサブパッケージ(おそらくscipy.signal)にあると思います。しかし、私はSciPyシキットのコレクションを最初に見ます、関連するscikitを特定し、そこで目的の機能を探します。

ScikitsはNumPy / SciPyに基づいて独自に開発されたパッケージであり、特定の技術分野(例:scikits-imagescikits-learnなど)を対象としています。これらのいくつか(特に、数値最適化のための素晴らしいOpenOpt)は高く評価されていました。比較的新しいscikitsルーブリックの下に住むことを選択するずっと前に、成熟したプロジェクト。上記のScikitsホームページには、約30のそのようなscikitsがリストされていますが、少なくともそのうちのいくつかは、現在活発に開発されていません。

このアドバイスに従うと、scikits-timeseriesにつながります。ただし、そのパッケージは現在活発に開発されていません。実際、Pandasは、事実上の NumPyベースの時系列ライブラリであるAFAIKになりました。

パンダには、移動平均を計算するために使用できるいくつかの関数があります。これらの中で最も単純なものは、おそらくrolling_meanで、次のように使用します。

>>> # the recommended syntax to import pandas
>>> import pandas as PD
>>> import numpy as NP

>>> # prepare some fake data:
>>> # the date-time indices:
>>> t = PD.date_range('1/1/2010', '12/31/2012', freq='D')

>>> # the data:
>>> x = NP.arange(0, t.shape[0])

>>> # combine the data & index into a Pandas 'Series' object
>>> D = PD.Series(x, t)

次に、Seriesオブジェクトとウィンドウサイズを渡して、関数rolling_meanを呼び出します。これは、以下の例では10日です。

>>> d_mva = PD.rolling_mean(D, 10)

>>> # d_mva is the same size as the original Series
>>> d_mva.shape
    (1096,)

>>> # though obviously the first w values are NaN where w is the window size
>>> d_mva[:3]
    2010-01-01         NaN
    2010-01-02         NaN
    2010-01-03         NaN

それが機能したことを確認します。たとえば、元のシリーズの値10〜15とローリング平均で平滑化された新しいシリーズの値を比較します。

>>> D[10:15]
     2010-01-11    2.041076
     2010-01-12    2.041076
     2010-01-13    2.720585
     2010-01-14    2.720585
     2010-01-15    3.656987
     Freq: D

>>> d_mva[10:20]
      2010-01-11    3.131125
      2010-01-12    3.035232
      2010-01-13    2.923144
      2010-01-14    2.811055
      2010-01-15    2.785824
      Freq: D

関数rolling_meanは、約12個ほどの他の関数とともに、ルーブリック移動ウィンドウ関数の下のPandasドキュメントに非公式にグループ化されています。パンダの2番目の関連する関数グループは、指数加重関数と呼ばれます(たとえば、指数移動する加重平均を計算するewma)。この2番目のグループが最初のグループ(移動ウィンドウ関数)に含まれていないのは、指数関数的に重み付けされた変換が固定長ウィンドウに依存していないためと考えられます


6
パンダは、移動ウィンドウ機能の強力なラインナップを持っています。しかし、単純な移動平均ではオーバーヘッドが少し多すぎるように思えます。
Jaime

6
移動平均を計算することは、OPや他のほぼすべての人にとって独立した要件であるとは思えません。移動平均を計算する必要がある場合、ほぼ確実に時系列があります。つまり、日時インデックスをデータに適合させることができるデータ構造が必要であり、それが参照する「オーバーヘッド」です。
ダグ

2
まず、この非常に有益な回答を書くために時間を割いていただきありがとうございます。確かに、時系列を含まない移動平均の用途はわかりません。ただし、これを日時または特定のサンプリング間隔(不明な場合もあります)に合わせる必要があるという意味ではありません
goncalopp

3
パンダが依存関係として重すぎると思われる場合は、移動平均関数がボトルネックライブラリに抽出されたことを追加したかっただけです。
robochat 14

4
「rolling_mean」はパンダの一部ではなくなりました。代わりに「rolling」を使用して返信をご覧ください
Vladtn

59

これを実現する簡単な方法は、を使用することnp.convolveです。この背後にある考え方は、離散畳み込みの計算方法を活用し、それを使用してローリング平均を返すことです。これは、一連のnp.ones、必要なスライディングウィンドウの長さに等しい長さの。

これを行うには、次の関数を定義します。

def moving_average(x, w):
    return np.convolve(x, np.ones(w), 'valid') / w

この関数は、シーケンスxと長さのシーケンスを畳み込みwます。選択されたものmodevalid、畳み込み積がシーケンスが完全にオーバーラップするポイントに対してのみ与えられるようになっていることに注意してください。


いくつかの例:

x = np.array([5,3,8,10,2,1,5,1,0,2])

長さのウィンドウを持つ移動平均の場合、次のよう2になります。

moving_average(x, 2)
# array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])

そして、長さのウィンドウの場合4

moving_average(x, 4)
# array([6.5 , 5.75, 5.25, 4.5 , 2.25, 1.75, 2.  ])

どのように機能しconvolveますか?

離散畳み込みの計算方法をさらに詳しく見てみましょう。次の関数np.convolveは、出力値の計算方法を再現することを目的としています。

def mov_avg(x, w):
    for m in range(len(x)-(w-1)):
        yield sum(np.ones(w) * x[m:m+w]) / w 

これは、上記と同じ例の場合も次のようになります。

list(mov_avg(x, 2))
# [4.0, 5.5, 9.0, 6.0, 1.5, 3.0, 3.0, 0.5, 1.0]

したがって、各ステップで行われていることは、1の配列と現在のウィンドウの間の内積を取ることです。この場合、シーケンスのをnp.ones(w)直接取っているのでsum、による乗算は不必要です。

以下は、最初の出力が少し明確になるように計算される方法の例です。次のウィンドウが必要だとしますw=4

[1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*5 + 1*3 + 1*8 + 1*10) / w = 6.5

また、次の出力は次のように計算されます。

  [1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*3 + 1*8 + 1*10 + 1*2) / w = 5.75

など、すべてのオーバーラップが実行されると、シーケンスの移動平均を返します。


これはいい考えです!nが小さい場合は@Jaimeの回答よりも高速ですが、nが大きい場合は遅くなります。
フェリペジェラール

@FelipeGerardに感謝!はい、コメントで指摘されているように、このアプローチは他のいくつかの厄介なソリューションほど効率的ではないかもしれませんが、そのシンプルさと簡潔さを考えると、将来の訪問者のための代替手段として最適です
yatu

出力配列を入力と同じサイズにすると便利な場合があります。これについては、mode='valid'で置き換えることができます'same'。この場合、エッジポイントはゼロに引き寄せられます。
Ilia Barahovski

15

これを行うためのさまざまな方法と、いくつかのベンチマークを以下に示します。最良の方法は、他のライブラリの最適化されたコードを使用するバージョンです。このbottleneck.move_mean方法は、おそらくすべての面で最良です。このscipy.convolveアプローチはまた、非常に高速で拡張可能であり、構文的にも概念的にも単純ですが、非常に大きなウィンドウ値に対しては適切にスケーリングされません。numpy.cumsum純粋な方法が必要な場合は、この方法が適していますnumpyアプローチ。

注:これらの一部(例:)bottleneck.move_meanは中央に配置されておらず、データがシフトされます。

import numpy as np
import scipy as sci
import scipy.signal as sig
import pandas as pd
import bottleneck as bn
import time as time

def rollavg_direct(a,n): 
    'Direct "for" loop'
    assert n%2==1
    b = a*0.0
    for i in range(len(a)) :
        b[i]=a[max(i-n//2,0):min(i+n//2+1,len(a))].mean()
    return b

def rollavg_comprehension(a,n):
    'List comprehension'
    assert n%2==1
    r,N = int(n/2),len(a)
    return np.array([a[max(i-r,0):min(i+r+1,N)].mean() for i in range(N)]) 

def rollavg_convolve(a,n):
    'scipy.convolve'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float')/n, 'same')[n//2:-n//2+1]  

def rollavg_convolve_edges(a,n):
    'scipy.convolve, edge handling'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float'), 'same')/sci.convolve(np.ones(len(a)),np.ones(n), 'same')  

def rollavg_cumsum(a,n):
    'numpy.cumsum'
    assert n%2==1
    cumsum_vec = np.cumsum(np.insert(a, 0, 0)) 
    return (cumsum_vec[n:] - cumsum_vec[:-n]) / n

def rollavg_cumsum_edges(a,n):
    'numpy.cumsum, edge handling'
    assert n%2==1
    N = len(a)
    cumsum_vec = np.cumsum(np.insert(np.pad(a,(n-1,n-1),'constant'), 0, 0)) 
    d = np.hstack((np.arange(n//2+1,n),np.ones(N-n)*n,np.arange(n,n//2,-1)))  
    return (cumsum_vec[n+n//2:-n//2+1] - cumsum_vec[n//2:-n-n//2]) / d

def rollavg_roll(a,n):
    'Numpy array rolling'
    assert n%2==1
    N = len(a)
    rolling_idx = np.mod((N-1)*np.arange(n)[:,None] + np.arange(N), N)
    return a[rolling_idx].mean(axis=0)[n-1:] 

def rollavg_roll_edges(a,n):
    # see /programming/42101082/fast-numpy-roll
    'Numpy array rolling, edge handling'
    assert n%2==1
    a = np.pad(a,(0,n-1-n//2), 'constant')*np.ones(n)[:,None]
    m = a.shape[1]
    idx = np.mod((m-1)*np.arange(n)[:,None] + np.arange(m), m) # Rolling index
    out = a[np.arange(-n//2,n//2)[:,None], idx]
    d = np.hstack((np.arange(1,n),np.ones(m-2*n+1+n//2)*n,np.arange(n,n//2,-1)))
    return (out.sum(axis=0)/d)[n//2:]

def rollavg_pandas(a,n):
    'Pandas rolling average'
    return pd.DataFrame(a).rolling(n, center=True, min_periods=1).mean().to_numpy()

def rollavg_bottlneck(a,n):
    'bottleneck.move_mean'
    return bn.move_mean(a, window=n, min_count=1)

N = 10**6
a = np.random.rand(N)
functions = [rollavg_direct, rollavg_comprehension, rollavg_convolve, 
        rollavg_convolve_edges, rollavg_cumsum, rollavg_cumsum_edges, 
        rollavg_pandas, rollavg_bottlneck, rollavg_roll, rollavg_roll_edges]

print('Small window (n=3)')
%load_ext memory_profiler
for f in functions : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[0:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,1001)

print('\nMemory\n')
print('Small window (n=3)')
N = 10**7
a = np.random.rand(N)
%load_ext memory_profiler
for f in functions[2:] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[2:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,1001)

タイミング、小ウィンドウ(n = 3)

Direct "for" loop : 

4.14 s ± 23.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
3.96 s ± 27.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
1.07 ms ± 26.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

scipy.convolve, edge handling : 
4.68 ms ± 9.69 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum : 
5.31 ms ± 5.11 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.52 ms ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.85 ms ± 9.63 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.3 ms ± 12.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy array rolling : 
31.3 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Numpy array rolling, edge handling : 
61.1 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

タイミング、大きなウィンドウ(n = 1001)

Direct "for" loop : 
4.67 s ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
4.46 s ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
103 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

scipy.convolve, edge handling : 
272 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

numpy.cumsum : 
5.19 ms ± 12.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.7 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.67 ms ± 199 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.31 ms ± 15.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

メモリ、小さなウィンドウ(n = 3)

The memory_profiler extension is already loaded. To reload it, use:
  %reload_ext memory_profiler

scipy.convolve : 
peak memory: 362.66 MiB, increment: 73.61 MiB

scipy.convolve, edge handling : 
peak memory: 510.24 MiB, increment: 221.19 MiB

numpy.cumsum : 
peak memory: 441.81 MiB, increment: 152.76 MiB

numpy.cumsum, edge handling : 
peak memory: 518.14 MiB, increment: 228.84 MiB

Pandas rolling average : 
peak memory: 449.34 MiB, increment: 160.02 MiB

bottleneck.move_mean : 
peak memory: 374.17 MiB, increment: 75.54 MiB

Numpy array rolling : 
peak memory: 661.29 MiB, increment: 362.65 MiB

Numpy array rolling, edge handling : 
peak memory: 1111.25 MiB, increment: 812.61 MiB

メモリ、大きなウィンドウ(n = 1001)

scipy.convolve : 
peak memory: 370.62 MiB, increment: 71.83 MiB

scipy.convolve, edge handling : 
peak memory: 521.98 MiB, increment: 223.18 MiB

numpy.cumsum : 
peak memory: 451.32 MiB, increment: 152.52 MiB

numpy.cumsum, edge handling : 
peak memory: 527.51 MiB, increment: 228.71 MiB

Pandas rolling average : 
peak memory: 451.25 MiB, increment: 152.50 MiB

bottleneck.move_mean : 
peak memory: 374.64 MiB, increment: 75.85 MiB

11

パンダを使用したこの回答rolling_meanは、パンダの一部ではなくなったため、上から変更されています

# the recommended syntax to import pandas
import pandas as pd
import numpy as np

# prepare some fake data:
# the date-time indices:
t = pd.date_range('1/1/2010', '12/31/2012', freq='D')

# the data:
x = np.arange(0, t.shape[0])

# combine the data & index into a Pandas 'Series' object
D = pd.Series(x, t)

次に、データrollingサイズの関数をウィンドウサイズで呼び出します。これは、以下の例では10日です。

d_mva10 = D.rolling(10).mean()

# d_mva is the same size as the original Series
# though obviously the first w values are NaN where w is the window size
d_mva10[:11]

2010-01-01    NaN
2010-01-02    NaN
2010-01-03    NaN
2010-01-04    NaN
2010-01-05    NaN
2010-01-06    NaN
2010-01-07    NaN
2010-01-08    NaN
2010-01-09    NaN
2010-01-10    4.5
2010-01-11    5.5
Freq: D, dtype: float64

5

これはボトルネックを使用して簡単に解決できると思います

以下の基本的なサンプルを参照してください。

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=(5, 7))
mm = bn.move_mean(a, window=2, min_count=1)

これにより、各軸に沿った移動平均が得られます。

  • 「mm」は「a」の移動平均です。

  • 「ウィンドウ」は、移動平均で考慮するエントリの最大数です。

  • "min_count"は、移動平均(最初の要素の場合、または配列にnan値がある場合など)について検討するエントリの最小数です。

良い点は、ボトルネックがナンの値を処理するのに役立ち、非常に効率的であることです。


2

エッジの条件を注意深く管理したい場合(エッジで利用可能な要素からのみ平均を計算する)、次の関数でうまくいきます。

import numpy as np

def running_mean(x, N):
    out = np.zeros_like(x, dtype=np.float64)
    dim_len = x.shape[0]
    for i in range(dim_len):
        if N%2 == 0:
            a, b = i - (N-1)//2, i + (N-1)//2 + 2
        else:
            a, b = i - (N-1)//2, i + (N-1)//2 + 1

        #cap indices to min and max indices
        a = max(0, a)
        b = min(dim_len, b)
        out[i] = np.mean(x[a:b])
    return out

>>> running_mean(np.array([1,2,3,4]), 2)
array([1.5, 2.5, 3.5, 4. ])

>>> running_mean(np.array([1,2,3,4]), 3)
array([1.5, 2. , 3. , 3.5])

1
for i in range(len(Data)):
    Data[i, 1] = Data[i-lookback:i, 0].sum() / lookback

このコードを試してください。私はそれがより簡単で、仕事をしていると思います。ルックバックは移動平均のウィンドウです。

Data[i-lookback:i, 0].sum()、私は入れている0データセットの最初の列を参照することではなく、あなたが複数の列を持っている場合には、あなたが好きな任意の列を置くことができます。


0

私は実際に、受け入れられた答えとは少し異なる動作を望んでいました。sklearnパイプラインの移動平均特徴抽出器を作成していたので、移動平均の出力が入力と同じ次元であることを要求しました。私が欲しいのは、移動平均が系列が一定であると仮定することです。つまり、[1,2,3,4,5]ウィンドウ2の移動平均は[1.5,2.5,3.5,4.5,5.0]です。ます。

列ベクトル(私のユースケース)の場合

def moving_average_col(X, n):
  z2 = np.cumsum(np.pad(X, ((n,0),(0,0)), 'constant', constant_values=0), axis=0)
  z1 = np.cumsum(np.pad(X, ((0,n),(0,0)), 'constant', constant_values=X[-1]), axis=0)
  return (z1-z2)[(n-1):-1]/n

そしてアレイのために

def moving_average_array(X, n):
  z2 = np.cumsum(np.pad(X, (n,0), 'constant', constant_values=0))
  z1 = np.cumsum(np.pad(X, (0,n), 'constant', constant_values=X[-1]))
  return (z1-z2)[(n-1):-1]/n

もちろん、パディングに定数値を想定する必要はありませんが、ほとんどの場合、そうすることで十分です。


0

talibには、単純な移動平均ツールと他の同様の平均化ツール(つまり、指数移動平均)が含まれています。以下では、この方法を他のいくつかのソリューションと比較しています。


%timeit pd.Series(np.arange(100000)).rolling(3).mean()
2.53 ms ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit talib.SMA(real = np.arange(100000.), timeperiod = 3)
348 µs ± 3.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit moving_average(np.arange(100000))
638 µs ± 45.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

注意点の1つは、実数にはの要素が必要であることですdtype = float。そうでなければ、次のエラーが発生します

例外:実数は2倍ではありません


0

以下は、numba(型を気にする)を使用した高速実装です。シフトした場所にはナンが含まれていることに注意してください。

import numpy as np
import numba as nb

@nb.jit(nb.float64[:](nb.float64[:],nb.int64),
        fastmath=True,nopython=True)
def moving_average( array, window ):    
    ret = np.cumsum(array)
    ret[window:] = ret[window:] - ret[:-window]
    ma = ret[window - 1:] / window
    n = np.empty(window-1); n.fill(np.nan)
    return np.concatenate((n.ravel(), ma.ravel())) 

これは最初にナンを返します。
アダムエリクソン

0

移動平均

  • iでの配列を反転し、単純にiからnまでの平均を取ります。

  • リスト内包表記を使用して、その場でミニ配列を生成します。

x = np.random.randint(10, size=20)

def moving_average(arr, n):
    return [ (arr[:i+1][::-1][:n]).mean() for i, ele in enumerate(arr) ]
n = 5

moving_average(x, n)

0

私は、別の回答のコメントで言及されているように、受け入れられた回答のソリューション、出力と入力の長さが同じになるようにわずかに変更pandasされたバージョン、または'バージョンのいずれかを使用します。ここでは、将来の参考のために、両方を再現可能な例で要約します。

import numpy as np
import pandas as pd

def moving_average(a, n):
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret / n

def moving_average_centered(a, n):
    return pd.Series(a).rolling(window=n, center=True).mean().to_numpy()

A = [0, 0, 1, 2, 4, 5, 4]
print(moving_average(A, 3))    
# [0.         0.         0.33333333 1.         2.33333333 3.66666667 4.33333333]
print(moving_average_centered(A, 3))
# [nan        0.33333333 1.         2.33333333 3.66666667 4.33333333 nan       ]

0

以下の解決策をnumpyの合計を使用する解決策と比較すると、これはほぼ半分の時間しかかかりません。これは、cumsumを実行してすべての減算を実行するために配列全体を調べる必要がないためです。さらに、配列が巨大で数が膨大な場合(オーバーフローの可能性)、cumsumは「危険」になる可能性があります。もちろん、ここにも危険が存在しますが、少なくとも、必要な数だけ合計されます。

def moving_average(array_numbers, n):
    if n > len(array_numbers):
      return []
    temp_sum = sum(array_numbers[:n])
    averages = [temp_sum / float(n)]
    for first_index, item in enumerate(array_numbers[n:]):
        temp_sum += item - array_numbers[first_index]
        averages.append(temp_sum / float(n))
    return averages
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.