pandasの.rolling()を使うと、DataFrameやSeriesに対して、データ区間をずらしながら関数を適用できます。
しかし、いざ.rolling()を適用とすると次のような疑問に直面することも…
.rolling()で、データ区間のサイズや出力位置を調整するにはどうするの?- ユーザー定義関数などの任意の関数を適用するには?
- 日付データで
.rolling()ってできるの?
そこで、今回は.rolling()を使って、データ区間をずらしながら関数を適用する方法を図解、サンプルコード付きでわかりやすく解説していきます。
実例で見る!rollingの使い方
.rolling()メソッドを使用すると、データ区間をずらしながら関数を適用できます。
例えば、.rolling()で平均値を計算すると、移動平均の計算が簡単にできます。
基本的な使い方は、データ区間の大きさ, windowと、適用関数を設定します。
df.rolling( window ).func()window:ずらしていくデータ区間の大きさfunc:適用関数(mean,max,min,countなど)
サンプルコードを使用して、移動平均の計算例を確認してみましょう。
import pandas as pd
# 入力データフレームの作成
data_list = list(range(5))
df = pd.DataFrame(data={"Input":data_list},
index=pd.Index(pd.date_range('1/1/2022', periods=5), name="Date") )
# 入力用データフレームの中身
# print(df)
# Input
# Date
# 2022-01-01 0
# 2022-01-02 1
# 2022-01-03 2
# 2022-01-04 3
# 2022-01-05 4
# rolling適用|window=3、mean()関数の適用
df["MA"] = df["Input"].rolling(3).mean()
# 結果出力
print(df)
# Input MA
# Date
# 2022-01-01 0 NaN
# 2022-01-02 1 NaN
# 2022-01-03 2 1.0
# 2022-01-04 3 2.0
# 2022-01-05 4 3.0
この例では、"Input"列をもとに、3日間の移動平均を計算しています。
この例のように入力と同じDataFrameに結果を出力する場合の基本パターンは次の通りです。
df["出力列ラベル"] = df["入力列ラベル"].rolling( window ).func()
以下では、次の項目を詳しく説明していきます。
.rolling()の基本設定.rolling()で使用できる関数- 時系列データを
.rolling()する方法
rollingの基本設定
.rolling()の基本設定用の引数は次の通りです。
| 設定内容 | 仮引数名(=デフォルト値) | 設定例・適用 |
|---|---|---|
| データ区間(窓)の大きさ | window |
int: 窓の大きさ |
| 計算に必要な最小データ数 | min_periods( = window) |
int: データ数がwindow未満でも、min_periods個以上で計算される |
| データ出力位置の変更 | center( = False) |
False: 窓の最下端位置に出力True: 窓の中央位置に出力 |
| 窓の移動方向の指定 (行or列方向) |
axis( = 0) |
0 or "rows": 列方向に移動1 or "columns": 行方向に移動 |
次のサンプルデータを使用して、それぞれの.rolling()の詳細設定方法を解説していきます。
# データフレームの作成
data_list = list(range(5))
df = pd.DataFrame(data={"Input":data_list},
index=pd.Index(pd.date_range('1/1/2022', periods=5), name="Date") )
# 結果出力
print(df)
# Input
# Date
# 2022-01-01 0
# 2022-01-02 1
# 2022-01-03 2
# 2022-01-04 3
# 2022-01-05 4
窓の大きさの変更|window
関数を適用する窓の大きさは、windowで指定します。
windowの値を変更して、2日間移動平均、3日間移動平均を計算してみます。
df["2MA"] = df["Input"].rolling(2).mean()
df["3MA"] = df["Input"].rolling(3).mean()
print(df)
# Input 2MA 3MA
# Date
# 2022-01-01 0 NaN NaN
# 2022-01-02 1 0.5 NaN
# 2022-01-03 2 1.5 1.0
# 2022-01-04 3 2.5 2.0
# 2022-01-05 4 3.5 3.0
windowの値に応じた移動平均が計算されていますね。
データ上端でwindowよりもデータ数が少ない区間では、NaNが出力される点に注意しましょう。
windowを覚えておけば、とりあえず.rolling()の基本はOKですね。
以下で、その他の設定方法について解説していきます。
計算に必要な最小データ数 |min_periods
データ上端ではwindowよりもデータ数が少ないのでNaNになってしまいます。
min_periodsを指定すると、データ数がwindow未満でも、min_periods以上のデータ数があれば関数を適用してくれます。
例として、window=4、min_periods=2の挙動を確認してみましょう。
df["mp_def"] = df["Input"].rolling(4).mean()
df["mp2"] = df["Input"].rolling(4, min_periods=2).mean()
print(df)
# Input mp_def mp2
# Date
# 2022-01-01 0 NaN NaN
# 2022-01-02 1 NaN 0.5
# 2022-01-03 2 NaN 1.0
# 2022-01-04 3 1.5 1.5
# 2022-01-05 4 2.5 2.5
min_periodsを指定しないと、データ数がwindow未満の場合NaNとなっています。
一方、min_periodsを指定すると、min_periods以上データ数があれば、ある分だけで関数を適用していますね。
出力データの上部でNaNを出力したくない場合に便利ですね。
データ出力位置の変更|center
デフォルトでは、窓の最下部の位置に結果を出力します。
一方、center=Trueを指定すると窓の中心位置に結果を出力します。
実際にサンプルコードで挙動の違いを比較してみましょう。
df["right"] = df["Input"].rolling(3).mean()
df["center"] = df["Input"].rolling(3, center=True).mean()
print(df)
# Input right center
# Date
# 2022-01-01 0 NaN NaN
# 2022-01-02 1 NaN 1.0
# 2022-01-03 2 1.0 2.0
# 2022-01-04 3 2.0 3.0
# 2022-01-05 4 3.0 NaN
デフォルトでは窓の一番下の行に結果が出力されています(0, 1, 2の平均1.0は2の行に)。
一方、center=Trueでは、窓の中心行に出力されています(0, 1, 2の平均1.0は1の行に)。
窓のサイズが偶数の場合には、中心より一つ下の位置に結果を出力します。
df["right"] = df["Input"].rolling(4).mean()
df["center"] = df["Input"].rolling(4, center=True).mean()
print(df)
# Input right center
# Date
# 2022-01-01 0 NaN NaN
# 2022-01-02 1 NaN NaN
# 2022-01-03 2 NaN 1.5
# 2022-01-04 3 1.5 2.5
# 2022-01-05 4 2.5 NaN
窓の移動方向の指定|axis
デフォルトでは、列方向に窓を移動させて計算を行います。
一方、axis=1 or "columns"を指定すると、行方向に計算を行うようになります。
次のサンプルコードで計算結果を比較してみましょう。
# 複数列のdfの用意
data_list=np.array([0,10,20,30])
df = pd.DataFrame({"A":data_list, "B":2*data_list, "C":3*data_list, "D":4*data_list})
print(df)
# A B C D
# 0 0 0 0 0
# 1 10 10 10 10
# 2 20 20 20 20
# 3 30 30 30 30
# 列方向にrolling(デフォルト)
df_row_direction = df.rolling(window=2).sum()
print(df_row_direction)
# A B C D
# 0 NaN NaN NaN NaN
# 1 10.0 20.0 30.0 40.0
# 2 30.0 60.0 90.0 120.0
# 3 50.0 100.0 150.0 200.0
# 行方向にrolling(axis=1 or "columns")
df_col_direction = df.rolling(window=2, axis="columns").sum()
print(df_col_direction)
# A B C D
# 0 NaN 0.0 0.0 0.0
# 1 NaN 30.0 50.0 70.0
# 2 NaN 60.0 100.0 140.0
# 3 NaN 90.0 150.0 210.0
想定通り、列方向に計算が行われましたね。
rollingで使える関数まとめ
.rolling()に適用できる主な関数は下表の通りです。
| 関数名 | メソッド名 |
|---|---|
| 平均 | .mean() |
| 最大 | .max() |
| 最小 | .min() |
| 中央値 | .median() |
| 標準偏差 | .std() |
| データ数 | .count() |
| 任意の関数の適用 | .apply() |
| 任意の関数の適用(複数関数・複数列) | .aggregate() or .agg() |
以下で、.mean()、.max()、.min()、.std()を適用した例を紹介します。
df["mean"] = df["Input"].rolling(4).mean()
df["max"] = df["Input"].rolling(4).max()
df["min"] = df["Input"].rolling(4).min()
df["count"] = df["Input"].rolling(4).count()
print(df)
# Input mean max min count
# Date
# 2022-01-01 0 NaN NaN NaN 1.0
# 2022-01-02 1 NaN NaN NaN 2.0
# 2022-01-03 2 NaN NaN NaN 3.0
# 2022-01-04 3 1.5 3.0 0.0 4.0
# 2022-01-05 4 2.5 4.0 1.0 4.0
組み込みの関数であれば簡単に適用できますね。
その他の組み込み関数については、公式ドキュメントを参照してください。
次にやや応用編として、次の2つの特別な関数の使い方を紹介します。
.apply()で任意の関数を適用する方法.aggregate()で同時に複数関数を適用する方法.aggregate()で列毎に異なる関数を適用する方法
任意の関数でrolling|.apply()
.apply()を使用すると、任意の関数で.rolling()できます。
df.resample("集計期間").apply(function)functionはarray_likeを引数にする関数|function( array_like )
例として、NumPyのnp.sum()関数を適用してみます。
df["sum"] = df["Input"].rolling(3).apply(np.sum)
print(df)
# Input sum
# Date
# 2022-01-01 0 NaN
# 2022-01-02 1 NaN
# 2022-01-03 2 3.0
# 2022-01-04 3 6.0
# 2022-01-05 4 9.0
次に自作の関数, my_funcを適用する例を紹介します。
自作関数, my_funcは、引数がarray_likeとなるように作成します。
def my_func(array_like):
return (max(array_like) - min(array_like))
x = [2, 10, 3]
print(my_func(x))
# 8
この関数は、引数のarray_likeの最大値と最小値の差を計算する関数ですね。
この自作関数を.rolling.apply()で適用してみましょう。
df["my_func"] = df["Input"].rolling(3).apply(my_func)
print(df)
# Input my_func
# Date
# 2022-01-01 0 NaN
# 2022-01-02 1 NaN
# 2022-01-03 2 2.0
# 2022-01-04 3 2.0
# 2022-01-05 4 2.0
.apply()を使えば、自作関数での集計も簡単に実行できますね。
同時に複数の関数適用|.agg()
.agg()メソッドを使用すると、同時に複数の関数を適用できます。
引数として、集計名("max"など)または関数名(np.sumなど)のリストを渡します。
df.rolling(window).agg([ "集計名1", "集計名2", 関数名3, 関数名4…])
df_res = df.rolling(3).agg(['min', 'max', np.sum])
print(df_res)
# Input
# min max sum
# Date
# 2022-01-01 NaN NaN NaN
# 2022-01-02 NaN NaN NaN
# 2022-01-03 0.0 2.0 3.0
# 2022-01-04 1.0 3.0 6.0
# 2022-01-05 2.0 4.0 9.0
関数が複数なので、マルチインデックスになっていますね。
列ごとに異なる関数適用|.agg({列名:集計方法})
ある列は合計、ある列は平均を計算したい場合もあるかと思います。
.agg()メソッドを使用すると、列ごとに異なる集計をすることも可能です。
その場合、引数として{列名: 適用関数}を要素とする辞書を渡します。
df.rolling(window).agg({"列1": 集計方法1, "列2": 集計方法2, …})適用関数:関数名("max"など)、関数(np.sumなど)またはそれらのリスト
各列に異なる集計を適用した例をみて見ましょう。
# rolling用データ準備
data_list=np.array([0,10,20,30])
df = pd.DataFrame({"A":data_list, "B":2*data_list, "C":3*data_list})
print(df)
# A B C
# 0 0 0 0
# 1 10 20 30
# 2 20 40 60
# 3 30 60 90
# 列毎に異なる関数適用
df_res = df.rolling(3).agg({"A":"sum", "B": max, "C":np.sum})
print(df_res)
# A B C
# 0 NaN NaN NaN
# 1 NaN NaN NaN
# 2 30.0 40.0 90.0
# 3 60.0 60.0 180.0
各列に、価格だったり、個数だったり、単価だったり、いろいろなデータが混ざっているときの集計時に力を発揮しそうな機能ですね。
時系列データでrollingする方法
IndexがDateTime形式の場合、windowに日数や時間を設定することも可能です。
| 集計期間 | 入力形式 | 入力例(1日の場合) |
|---|---|---|
n日間 |
"nD" |
"1D" |
n時間 |
"nH" |
"24H" |
n分間 |
"nmin" or "nT" |
"3600min" or "3600T" |
n秒間 |
"nS" |
"216000S" |
次の時系列データを用いて、時系列データの.rolling()適用例を確認してみましょう。
# 入力データの用意
data_list = list(range(5))
# データフレームの作成
df = pd.DataFrame(data={"Input":data_list},
index=pd.Index(pd.date_range('1/1/2022', periods=5, freq="2D"), name="Date") )
print(df)
# Input
# Date
# 2022-01-01 0
# 2022-01-03 1
# 2022-01-05 2
# 2022-01-07 3
# 2022-01-09 4
このサンプルデータは、日にちが一日ごとになっている点に注意してください。
このサンプルに対して、"3D"および"5D"で.rolling()してみます。
df["3D"] = df["Input"].rolling("3D").mean()
df["5D"] = df["Input"].rolling("5D").mean()
# 結果出力
print(df)
# Input 3D 5D
# Date
# 2022-01-01 0 0.0 0.0
# 2022-01-03 1 0.5 0.5
# 2022-01-05 2 1.5 1.0
# 2022-01-07 3 2.5 2.0
# 2022-01-09 4 3.5 3.0
日にちが飛んでいる場合でも、設定した日にちごとに.rolling()していますね。
おわりに|padans関連おススメ追加コンテンツ
今回はpandasの.rolling()について解説しました。
pandasは便利すぎて操作方法がわかりにくいことがよくあります…。
結局はコツコツ学ぶのが、pandasマスターの近道ですよね!≫【ブログカテゴリー:pandas】
