pandasで窓関数を適用するrollingを使って移動平均などを算出
pandas.DataFrame
, pandas.Series
に窓関数(Window Function)を適用するにはrolling()
を使う。
- pandas.DataFrame.rolling — pandas 0.23.3 documentation
- pandas.Series.rolling — pandas 0.23.3 documentation
窓関数はフィルタをデザインする際などに使われるが、単純に移動平均線を算出(前後のデータの平均を算出)したりするのにも使える。
ここでは以下の内容について説明する。
rolling()
の基本的な使い方- Windowの幅を指定: 引数
window
- Windowの中心に結果の値を格納する: 引数
center
- 最小データ個数を指定: 引数
min_periods
- 窓関数の種類を指定: 引数
win_type
- 列方向に窓関数を適用: 引数
axis
- Windowの幅を指定: 引数
window.Rolling
型に適用できるメソッド- 時系列データにおける
rolling()
とresample()
rolling()の基本的な使い方
以下のpandas.Series
を例とする。
import pandas as pd
s = pd.Series(range(10))
print(s)
# 0 0
# 1 1
# 2 2
# 3 3
# 4 4
# 5 5
# 6 6
# 7 7
# 8 8
# 9 9
# dtype: int64
rolling()
メソッドを呼んでも何か値が算出されるわけではなく、window.Rolling
型のオブジェクトが返される。
print(s.rolling(3))
# Rolling [window=3,center=False,axis=0]
print(type(s.rolling(3)))
# <class 'pandas.core.window.rolling.Rolling'>
さらにメソッドを実行する必要がある。
print(s.rolling(3).sum())
# 0 NaN
# 1 NaN
# 2 3.0
# 3 6.0
# 4 9.0
# 5 12.0
# 6 15.0
# 7 18.0
# 8 21.0
# 9 24.0
# dtype: float64
生成したオブジェクトから計算用のメソッドを呼ぶということで、使い方のイメージとしてはgroupby()
に近い。
この例ではrolling(3)
で上から順に3個ずつデータが選択されsum()
で合計が算出される。
s[0]
,s[1]
,s[2]
の合計 → 返り値の[2]
に格納s[1]
,s[2]
,s[3]
の合計 → 返り値の[3]
に格納- …
s[6]
,s[7]
,s[8]
の合計 → 返り値の[8]
に格納s[7]
,s[8]
,s[9]
の合計 → 返り値の[9]
に格納
返り値の先頭2つ([0]
, [1]
)は3個分のデータが無いため欠損値NaN
となる。NaN
の扱いについては以下の記事を参照。
以下、rolling()
の引数について説明する。window.Rolling
型のメソッドについては後述。
Windowの幅を指定: 引数window
Windowの幅は第一引数window
に指定する。省略不可。
print(s.rolling(2).sum())
# 0 NaN
# 1 1.0
# 2 3.0
# 3 5.0
# 4 7.0
# 5 9.0
# 6 11.0
# 7 13.0
# 8 15.0
# 9 17.0
# dtype: float64
print(s.rolling(4).sum())
# 0 NaN
# 1 NaN
# 2 NaN
# 3 6.0
# 4 10.0
# 5 14.0
# 6 18.0
# 7 22.0
# 8 26.0
# 9 30.0
# dtype: float64
時系列データについては特殊な指定方法がある。後述。
Windowの中心に結果の値を格納する: 引数center
これまでの例のように、デフォルトではWindowの最後の位置に算出されたデータが格納される。
引数center=True
とすると、Windowの中心位置に算出されたデータが格納される。先頭と末尾のデータが足りない分は欠損値NaN
となる。
print(s.rolling(3, center=True).sum())
# 0 NaN
# 1 3.0
# 2 6.0
# 3 9.0
# 4 12.0
# 5 15.0
# 6 18.0
# 7 21.0
# 8 24.0
# 9 NaN
# dtype: float64
print(s.rolling(4, center=True).sum())
# 0 NaN
# 1 NaN
# 2 6.0
# 3 10.0
# 4 14.0
# 5 18.0
# 6 22.0
# 7 26.0
# 8 30.0
# 9 NaN
# dtype: float64
サンプルコードの前者(第一引数window=3
)は以下の通りになっている。
s[0]
,s[1]
,s[2]
の合計 → 返り値の[1]
に格納- ...
s[7]
,s[8]
,s[9]
の合計 → 返り値の[8]
に格納
後者(第一引数window=4
)は以下の通り。
s[0]
,s[1]
,s[2]
,s[3]
の合計 → 返り値の[2]
に格納- ...
s[6]
,s[7]
,s[8]
,s[9]
の合計 → 返り値の[8]
に格納
最小データ個数を指定: 引数min_periods
これまでの例のように、デフォルトではWindowに含まれるデータ個数が第一引数window
に満たない位置の結果は欠損値NaN
となる。
引数min_periods
に整数値を指定すると、その値の個数のデータが含まれていれば結果が算出される。
デフォルトでは上端のデータがNaN
となるが、min_periods
に小さい値を指定すると少ない個数のデータで結果が算出される。
print(s.rolling(3, min_periods=2).sum())
# 0 NaN
# 1 1.0
# 2 3.0
# 3 6.0
# 4 9.0
# 5 12.0
# 6 15.0
# 7 18.0
# 8 21.0
# 9 24.0
# dtype: float64
print(s.rolling(3, min_periods=1).sum())
# 0 0.0
# 1 1.0
# 2 3.0
# 3 6.0
# 4 9.0
# 5 12.0
# 6 15.0
# 7 18.0
# 8 21.0
# 9 24.0
# dtype: float64
窓関数の種類を指定: 引数win_type
窓関数の種類は引数win_type
で指定する。デフォルトは矩形窓で、Window内の全てのデータ点が一様に扱われる(全てのデータ点に対する重みが1
)。
使用できる窓関数の種類は公式ドキュメントのNotesを参照。
列方向に窓関数を適用: 引数axis
これまでの例はpandas.Series
だったがpandas.DataFrame
でも同様。
デフォルトでは列に対して、引数axis=1
とすると行に対して窓関数が適用される。
df = pd.DataFrame({'a': range(10), 'b': range(10, 0, -1),
'c': range(10, 20), 'd': range(20, 10, -1)})
print(df.rolling(2).sum())
# a b c d
# 0 NaN NaN NaN NaN
# 1 1.0 19.0 21.0 39.0
# 2 3.0 17.0 23.0 37.0
# 3 5.0 15.0 25.0 35.0
# 4 7.0 13.0 27.0 33.0
# 5 9.0 11.0 29.0 31.0
# 6 11.0 9.0 31.0 29.0
# 7 13.0 7.0 33.0 27.0
# 8 15.0 5.0 35.0 25.0
# 9 17.0 3.0 37.0 23.0
print(df.rolling(2, axis=1).sum())
# a b c d
# 0 NaN 10.0 20.0 30.0
# 1 NaN 10.0 20.0 30.0
# 2 NaN 10.0 20.0 30.0
# 3 NaN 10.0 20.0 30.0
# 4 NaN 10.0 20.0 30.0
# 5 NaN 10.0 20.0 30.0
# 6 NaN 10.0 20.0 30.0
# 7 NaN 10.0 20.0 30.0
# 8 NaN 10.0 20.0 30.0
# 9 NaN 10.0 20.0 30.0
window.Rolling型に適用できるメソッド
window.Rolling
型に適用できるメソッドの一覧は公式ドキュメントを参照。
平均値mean()
, 中央値median()
, 最小値min()
, 最大値max()
、標準偏差std()
などがある。
移動平均を算出したい場合はmean()
。
print(s.rolling(3).mean())
# 0 NaN
# 1 NaN
# 2 1.0
# 3 2.0
# 4 3.0
# 5 4.0
# 6 5.0
# 7 6.0
# 8 7.0
# 9 8.0
# dtype: float64
agg()
で複数の処理を同時に適用することもできる。agg()
はaggregate()
のエイリアス。どちらを使ってもよい。
リストで複数の処理を指定する。
各処理は、上記のwindow.Rolling
型のメソッド名を文字列で指定できるほか、呼び出し可能オブジェクトを指定することも可能。呼び出し可能オブジェクトとしては、組み込み関数やNumPyの関数、def
で定義した関数やラムダ関数を指定できる。
print(s.rolling(3).agg(['sum', 'mean', 'skew', 'cov',
max, min,
lambda x: max(x) - min(x)]))
# sum mean skew cov max min <lambda>
# 0 NaN NaN NaN NaN NaN NaN NaN
# 1 NaN NaN NaN NaN NaN NaN NaN
# 2 3.0 1.0 0.000000e+00 1.0 2.0 0.0 2.0
# 3 6.0 2.0 -7.993606e-15 1.0 3.0 1.0 2.0
# 4 9.0 3.0 2.398082e-14 1.0 4.0 2.0 2.0
# 5 12.0 4.0 -6.394885e-14 1.0 5.0 3.0 2.0
# 6 15.0 5.0 -7.993606e-14 1.0 6.0 4.0 2.0
# 7 18.0 6.0 1.918465e-13 1.0 7.0 5.0 2.0
# 8 21.0 7.0 2.238210e-13 1.0 8.0 6.0 2.0
# 9 24.0 8.0 -5.115908e-13 1.0 9.0 7.0 2.0
時系列データにおけるrolling()とresample()
以下のような隔日の時系列データを例とする。
import pandas as pd
df = pd.DataFrame({'value': range(1, 32, 2)},
index=pd.date_range('2018-01-01', '2018-01-31', freq='2D'))
print(df)
# value
# 2018-01-01 1
# 2018-01-03 3
# 2018-01-05 5
# 2018-01-07 7
# 2018-01-09 9
# 2018-01-11 11
# 2018-01-13 13
# 2018-01-15 15
# 2018-01-17 17
# 2018-01-19 19
# 2018-01-21 21
# 2018-01-23 23
# 2018-01-25 25
# 2018-01-27 27
# 2018-01-29 29
# 2018-01-31 31
時系列データだけの特徴
第一引数window
に数値を指定すると以下のようになる。これまで通り。
print(df.rolling(5).mean())
# value
# 2018-01-01 NaN
# 2018-01-03 NaN
# 2018-01-05 NaN
# 2018-01-07 NaN
# 2018-01-09 5.0
# 2018-01-11 7.0
# 2018-01-13 9.0
# 2018-01-15 11.0
# 2018-01-17 13.0
# 2018-01-19 15.0
# 2018-01-21 17.0
# 2018-01-23 19.0
# 2018-01-25 21.0
# 2018-01-27 23.0
# 2018-01-29 25.0
# 2018-01-31 27.0
時系列データの場合、第一引数window
にはD
(日)、H
(時)などのように頻度コードを文字列で指定することもできる。頻度コードについては以下の記事を参照。
'5D'
(5日間)を指定すると以下のようになる。
print(df.rolling('5D').mean())
# value
# 2018-01-01 1.0
# 2018-01-03 2.0
# 2018-01-05 3.0
# 2018-01-07 5.0
# 2018-01-09 7.0
# 2018-01-11 9.0
# 2018-01-13 11.0
# 2018-01-15 13.0
# 2018-01-17 15.0
# 2018-01-19 17.0
# 2018-01-21 19.0
# 2018-01-23 21.0
# 2018-01-25 23.0
# 2018-01-27 25.0
# 2018-01-29 27.0
# 2018-01-31 29.0
window
を頻度コードで指定した場合の結果の違いは2つ。
- 引数
min_periods=1
がデフォルトとなる - インデックスの日時を考慮してデータが選択される
2について2018-01-31
の結果を例とすると、
- 数値
5
で指定した場合2018-01-23
から2018-01-31
までの5個分のデータで算出
- 頻度コード
'5D'
で指定した場合2018-01-27
から2018-01-31
までの5日分のデータで算出(データの個数としては3個)
となる。
rolling()とresample()の違い
時系列データに対して任意の範囲のデータを集約するメソッドにresample()
がある。
rolling()
とresample()
の結果の違いは以下の通り。
print(df.rolling('5D').mean())
# value
# 2018-01-01 1.0
# 2018-01-03 2.0
# 2018-01-05 3.0
# 2018-01-07 5.0
# 2018-01-09 7.0
# 2018-01-11 9.0
# 2018-01-13 11.0
# 2018-01-15 13.0
# 2018-01-17 15.0
# 2018-01-19 17.0
# 2018-01-21 19.0
# 2018-01-23 21.0
# 2018-01-25 23.0
# 2018-01-27 25.0
# 2018-01-29 27.0
# 2018-01-31 29.0
print(df.resample('5D').mean())
# value
# 2018-01-01 3
# 2018-01-06 8
# 2018-01-11 13
# 2018-01-16 18
# 2018-01-21 23
# 2018-01-26 28
# 2018-01-31 31
元のデータ点ごとに指定範囲の統計量を算出するのがrolling()
で、指定範囲ごとにデータを集約するのがresample()
。
resample()
はその名の通りリサンプリングに使われる。詳細は以下の記事を参照。