LoginSignup
11
9

More than 5 years have passed since last update.

移動ウィンドウのためにpandasのrollingの代わりを探したときのメモ

Posted at

時系列データのテクニカル分析では、ウィンドウを移動させながら平均を取ったり、最大値や最小値を求めることが多いです。pandasだと、rollingで移動ウィンドウを指定してmean, max, minのメソッドで簡単に書けてしまいます。今回の記事は、pandasより速くできる方法はないかと探したときのメモです。

pandasのrollingを使う

まず、以下のようにnumpyのarrayとpandasのSeriesで乱数の時系列を作っておきます。

import numpy as np
import pandas as pd
a = np.random.randint(100, size=100000)
s = pd.Series(a)

移動ウィンドウでの平均(いわゆる単純移動平均)は、rollingにmeanメソッドを使って次のように書けます。

period=10 #期間
%timeit smean = s.rolling(period).mean()

実行時間は

100 loops, best of 3: 5.47 ms per loop

でした。次に移動ウィンドウでの最大値、最小値です。

%timeit smax = s.rolling(period).max()
%timeit smin = s.rolling(period).min()
100 loops, best of 3: 5.51 ms per loop
100 loops, best of 3: 5.53 ms per loop

実行時間は移動平均とほぼ同じです。

scipyのフィルタ関数を使う

移動平均はいわゆるFIRフィルタなので、scipyのlfilter関数が利用できます。

from scipy.signal import lfilter
%timeit amean = lfilter(np.ones(period)/period, 1, a)

重みをすべて1/periodにしたFIRフィルタとして計算させます。実行時間は

1000 loops, best of 3: 980 µs per loop

となりました。pandasの5倍以上速くなりました。さすがscipyです。

こんどは、最大値、最小値を求めたいのですが、それにぴったりの関数はなくて、使えそうなのが、order_filter関数でした。この関数は、指定したウィンドウ中で指定した順位の値を順次返す関数です。引数domainにウィンドウのマスク配列、引数rankに順位を指定します。ただし、時系列のサンプルを中心に対象なウィンドウとなるので、配列の前半だけに1を入れておきます。最小値の場合rank=0、最大値の場合、rank=period-1とすればOKです。

from scipy.signal import order_filter
domain = np.concatenate((np.ones(period), np.zeros(period-1)))
%timeit amax = order_filter(a, domain, period-1)
%timeit amin = order_filter(a, domain, 0)

実行結果は以下のようになりました。

10 loops, best of 3: 102 ms per loop
10 loops, best of 3: 102 ms per loop

こんどはpandasの20倍近く遅くなってしまいました。scipyの関数でもダメでした。やはり、任意の順位を出せるよう毎回ソートしているからでしょう。最大値、最小値を求めるのであれば、それ専用の関数がいいということでした。

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9