時系列「Ser」があり、ローリングウィンドウを使用してボラティリティ(標準偏差)を計算したいと考えています。私の現在のコードはこの形式で正しくそれを行います:
w=10
for timestep in range(length):
subSer=Ser[timestep:timestep+w]
mean_i=np.mean(subSer)
vol_i=(np.sum((subSer-mean_i)**2)/len(subSer))**0.5
volList.append(w_i)
これは私には非常に非効率的です。 Pandasには、このようなことを行うための組み込み機能がありますか?
_Series.rolling
_ を探しているようです。結果のオブジェクトに std
計算を適用できます:
_roller = Ser.rolling(w)
volList = roller.std(ddof=0)
_
ローリングウィンドウオブジェクトを再び使用する予定がない場合は、ワンライナーを作成できます。
_volList = Ser.rolling(w).std(ddof=0)
_
標準偏差の正規化はlen(Ser)-ddof
によるものであり、パンダではddof
のデフォルトは_ddof=0
_であるため、この場合は_1
_が必要であることを覚えておいてください。
通常、[ファイナンスタイプ]の人々は、ボラティリティを年率換算した価格の変化率で見積もります。
データフレームdf
に毎日の価格があり、1年に252取引日があるとすると、おそらく次のようなものが必要です。
df.pct_change().rolling(window_size).std()*(252**0.5)
これがNumPyアプローチの1つです-
# From http://stackoverflow.com/a/14314054/3293881 by @Jaime
def moving_average(a, n=3) :
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
return ret[n - 1:] / n
# From http://stackoverflow.com/a/40085052/3293881
def strided_app(a, L, S=1 ): # Window len = L, Stride len/stepsize = S
nrows = ((a.size-L)//S)+1
n = a.strides[0]
return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))
def rolling_meansqdiff_numpy(a, w):
A = strided_app(a, w)
B = moving_average(a,w)
subs = A-B[:,None]
sums = np.einsum('ij,ij->i',subs,subs)
return (sums/w)**0.5
サンプルの実行-
In [202]: Ser = pd.Series(np.random.randint(0,9,(20)))
In [203]: rolling_meansqdiff_loopy(Ser, w=10)
Out[203]:
[2.6095976701399777,
2.3000000000000003,
2.118962010041709,
2.022374841615669,
1.746424919657298,
1.7916472867168918,
1.3000000000000003,
1.7776388834631178,
1.6852299546352716,
1.6881943016134133,
1.7578395831246945]
In [204]: rolling_meansqdiff_numpy(Ser.values, w=10)
Out[204]:
array([ 2.60959767, 2.3 , 2.11896201, 2.02237484, 1.74642492,
1.79164729, 1.3 , 1.77763888, 1.68522995, 1.6881943 ,
1.75783958])
実行時テスト
ルーピーアプローチ-
def rolling_meansqdiff_loopy(Ser, w):
length = Ser.shape[0]- w + 1
volList= []
for timestep in range(length):
subSer=Ser[timestep:timestep+w]
mean_i=np.mean(subSer)
vol_i=(np.sum((subSer-mean_i)**2)/len(subSer))**0.5
volList.append(vol_i)
return volList
タイミング-
In [223]: Ser = pd.Series(np.random.randint(0,9,(10000)))
In [224]: %timeit rolling_meansqdiff_loopy(Ser, w=10)
1 loops, best of 3: 2.63 s per loop
# @Mad Physicist's vectorized soln
In [225]: %timeit Ser.rolling(10).std(ddof=0)
1000 loops, best of 3: 380 µs per loop
In [226]: %timeit rolling_meansqdiff_numpy(Ser.values, w=10)
1000 loops, best of 3: 393 µs per loop
7000x
に近いスピードアップで、ルーピーなアプローチよりも2つのベクトル化されたアプローチを使用できます。