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】