numpy/scipy では単純に移動平均を計算する関数がないようで、複雑な解につながります。
私の質問は 2 つあります。
- numpyで移動平均を(正しく)実装する最も簡単な方法は何ですか?
- これは些細なことではなく、エラーが発生しやすいように思われるため、このケースにバッテリーを含めない正当な理由はありますか?
単純な重み付けされていない移動平均が必要な場合は、 を使用して簡単に実装できます。これは、FFT ベースの方法よりも高速np.cumsum
である可能性が あります。
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 は特殊な機能ですでに少し肥大化しています。
NumPy に特定のドメイン固有の機能がないのは、おそらく NumPy の主な指示であるコア チームの規律と忠実さによるものです: N 次元配列 typeと、それらの配列を作成およびインデックス付けするための関数を提供します。多くの基本的な目的と同様に、これは小さくありません。NumPy は見事にそれを行います。
(はるかに) 大きいSciPyには、ドメイン固有のライブラリ ( SciPy 開発者によってサブパッケージと呼ばれる) のはるかに大きなコレクションが含まれています。たとえば、数値最適化 ( optimize )、信号処理 ( signal )、および積分計算 ( integrate )。
私の推測では、あなたが求めている関数は、少なくとも 1 つの SciPy サブパッケージ (おそらくscipy.signal ) にあると思います。ただし、最初にSciPy scikitsのコレクションを調べ、関連する scikit(s) を特定し、そこで目的の関数を探します。
scikitsは、NumPy/SciPy に基づいて独自に開発されたパッケージであり、特定の技術分野 (例: scikits-image、scikits-learnなど) を対象としています。これらのいくつか (特に、数値最適化のためのすばらしいOpenOpt ) は高く評価されました。成熟したプロジェクトは、比較的新しいscikitsルーブリックの下に置くことを選択するずっと前に行われました。上記のScikitsのホームページには、約 30 のそのようなscikitsがリストされていますが、そのうちの少なくともいくつかは現在活発に開発されていません。
このアドバイスに従えば、scikits-timeseriesにたどり着きます。ただし、そのパッケージは現在、活発に開発されていません。実際、Pandasは事実上の NumPyベースの時系列ライブラリになっています。
Pandasには、移動平均の計算に使用できる関数がいくつかあります。これらの中で最も単純なものはおそらく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 オブジェクトとwindow sizeを渡して関数roller_meanを呼び出します。以下の例では10 daysです。
>>> 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ドキュメントに非公式にグループ化されています。関連する Pandas の 2 番目の関数グループは、指数加重関数 (指数移動加重平均を計算するewmaなど) と呼ばれます。この 2 番目のグループが最初のグループ (移動ウィンドウ関数) に含まれていないという事実は、おそらく、指数加重変換が固定長ウィンドウに依存していないためです。
すべての答えは、事前計算されたリストの場合に焦点を当てているようです。数値が 1 つずつ入力される実際の実行ユースケースでは、最後の N 値を平均化するサービスを提供する単純なクラスを次に示します。
import numpy as np
class RunningAverage():
def __init__(self, stack_size):
self.stack = [0 for _ in range(stack_size)]
self.ptr = 0
self.full_cycle = False
def add(self,value):
self.stack[self.ptr] = value
self.ptr += 1
if self.ptr == len(self.stack):
self.full_cycle = True
self.ptr = 0
def get_avg(self):
if self.full_cycle:
return np.mean(self.stack)
else:
return np.mean(self.stack[:self.ptr])
利用方法:
N = 50 # size of the averaging window
run_avg = RunningAverage(N)
for i in range(1000):
value = <my computation>
run_avg.add(value)
if i % 20 ==0: # print once in 20 iters:
print(f'the average value is {run_avg.get_avg()}')
出力の長さが入力と同じになるようにわずかに変更された、受け入れられた回答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 ]
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
。そうしないと、次のエラーが発生します
例外: 実数は double ではありません