移動平均のなかで、単純移動平均(SMA)の次によく使われるのは指数移動平均(EMA)でしょう。EMAを使うテクニカル指標として有名なMACDをはじめ、EMAを複数回使うDEMA、TEMA、TriXというのもあるし、AMA、FrAMA、VIDyAのように適応型移動平均は、計算方法としてEMAを利用しています。
今回は、
IIRフィルタ型の移動平均をpandasとscipyで比較してみた
の続きとして、EMAのコードをいくつか比較してみます。
EMAの計算式
EMAを実際に使う際には期間をパラメータとして入れますが、ここではEMA本体のパフォーマンスを調べるために計算式を直接使います。
$$y(n)=\alpha x(n)+(1-\alpha)y(n-1)$$
つまり、EMAのパラメータは上式の$\alpha$とします。
pandasによる実装
まず、EMAをかける入力データを
Numba使用を前提とした単純移動平均のPythonコードについて
と同じくランダムウォークとして作っておきます。
import numpy as np
import pandas as pd
dn = np.random.randint(2, size=100000)*2-1
gwalk = np.cumprod(np.exp(dn*0.01))*100
pandasでは、Seriesクラスに変換してewm
とmean
メソッドで簡単にEMAを実行できます。ewm
のパラメータとしてalpha
を直接代入することもできます。alpha
は0から1の範囲で特に値による違いはないので、ここでは0.15を入れておきます。
def EMA1(x, alpha):
return pd.Series(x).ewm(alpha=alpha).mean()
%timeit y1 = EMA1(gwalk, 0.15)
実行時間は以下の通りです。
100 loops, best of 3: 8.62 ms per loop
scipyのlflterによる実装
IIRフィルタ型の移動平均をpandasとscipyで比較してみた
と同じようにscipyのフィルタ関数lfilterを使った実装です。
from scipy.signal import lfilter
def EMA2(x, alpha):
y,zf = lfilter([alpha], [1,alpha-1], x, zi=[x[0]*(1-alpha)])
return y
%timeit y2 = EMA2(gwalk, 0.15)
実行時間は次のようになりました。
1000 loops, best of 3: 631 µs per loop
前回の記事はここまでで、scipy速いということだったのですが、今回は続きがあります。
Numbaを利用した計算式の直接実装
EMAの計算式を直接コーディングします。ただし、そのままだとすごく遅くなるので、Numbaを使って高速化します。
from numba import jit
@jit(nopython=True)
def EMA3(x, alpha):
y = np.empty_like(x)
y[0] = x[0]
for i in range(1,len(x)):
y[i] = alpha*x[i] + (1-alpha)*y[i-1]
return y
%timeit y3 = EMA3(gwalk, 0.15)
@jit
の引数にnopython=True
をつけてエラーが出なければ高速化が期待できます。実際、実行時間は
1000 loops, best of 3: 227 µs per loop
となり、scipyより速くなりました。
EMAの場合も、Numbaを使えば直接コーディングが最速という結果となりました。