YutaKaのPython教室

Python の文法やライブラリ、API、環境構築について画像・動画・ソースコード付きで徹底解説!

pandas 移動平均も楽々計算!|rollingをわかりやすく解説!

pandas.rolling()を使うと、DataFrameSeriesに対して、データ区間をずらしながら関数を適用できます。

しかし、いざ.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=4min_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)
    • functionarray_likeを引数にする関数|function( array_like )

例として、NumPynp.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する方法

IndexDateTime形式の場合、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】

≫pandas 月ごと・週ごとの集計|resampleをわかりやすく解説!
≫pandas 月ごと・週ごとの集計|resampleをわかりやすく解説!
pandasで、DataFrameやSeriesのデータを月ごと週ごとなどで集計する方法を解説します。集計期間はどうやって設定するの?合計や平均などの集計方法はどうやって設定する?自作関数の適用方法、列毎に違う集計をする方法はあるの?こんな悩みを図解・サンプルコート付きでわかりやすく解決します!
www.yutaka-note.com/entry/pandas_resample
 
≫pandas 文字列⇒Datetime変換|時系列操作を簡単マスター!
≫pandas 文字列⇒Datetime変換|時系列操作を簡単マスター!
pandasで"文字列"をDatetimeに変換する方法、Datetimeの基本的な操作方法を解説します。"文字列"のままでは、日時関連の計算や条件判定が自由にできません…。Datetimeに変換して、日付の差分計算や便利な機能を使えるようにしましょう!
www.yutaka-note.com/entry/pandas_datetime
 
≫pandas インデックス名の設定・変更|パターン別にわかりやすく解説
≫pandas インデックス名の設定・変更|パターン別にわかりやすく解説
pandasのDataFrameでは、インデックス名やカラム名を使ってデータにアクセスしますね。しかし、インデックスやコラムの操作は少しわかりにくい部分も…。そこで、この記事では①インデックスの基本的な設定方法、変更方法、②インデックス番号のリセット方法をサンプルコード付きでわかりやすく解説!
www.yutaka-note.com/entry/pandas_index_setting
 
≫pandas インデックスの基本操作|要素にアクセス、
≫pandas インデックスの基本操作|要素にアクセス、
pandasのDataFrameでは、インデックス名やカラム名を使ってデータにアクセスしますね。しかし、インデックスやコラムの操作は少しわかりにくい部分も…。そこで、この記事では①インデックスの基本構造②インデックス内要素へのアクセス方法③基本的なインデックス操作(データ探索、並べ替え、重複処理、欠損値処理)をサンプルコード付きでわかりやすく解説!
www.yutaka-note.com/entry/pandas_index_manip
 
≫【レビュー】「Pythonによるデータ分析入門」| pandas開発者によるpandasユーザーのためのpandasの教科書!
≫【レビュー】「Pythonによるデータ分析入門」| pandas開発者によるpandasユーザーのためのpandasの教科書!
「Pythonによるデータ分析入門」を、最初から最後まで実際に実践してみたレビューです。具体的にどのようなことができるようになったかを実例付きで紹介します!・DataFrameの生成方法・欠損値の処理方法・グラフ化の方法気になる学習時間は…?
www.yutaka-note.com/entry/2019/12/07/230219
 
≫pandas DataFrameの行・列を抽出|loc, ilocなどわかりやすく解説!
≫pandas DataFrameの行・列を抽出|loc, ilocなどわかりやすく解説!
pandasのDataFrameを使うと、行と列で構成されたデータを簡単に取り扱うことができます。この記事では、「DataFrameの特定の行、列のデータを抽出する方法」、「インデックス参照、.loc、.iloc、at、iat[]の使い方」をわかりやすい図解付きで解説しています。
www.yutaka-note.com/entry/pandas_access
 
≫pandas 最大値・最小値を抽出|図解でわかるmax min idxmax idxmin
≫pandas 最大値・最小値を抽出|図解でわかるmax min idxmax idxmin
pandas DataFrame|最大値、最小値の取り扱いを図解でわかりやすく解説! ①max(), min()で最大値・最小値抽出。②idxmax, idxmin()で最大値・最小値の行名・列名取得。③nlargest, nsmmallestでn番目に大きい・小さい値を取得。
www.yutaka-note.com/entry/pandas_maxmin
 
≫pandas リストからDataFrameを生成|インデックスとコラムの設定も!
≫pandas リストからDataFrameを生成|インデックスとコラムの設定も!
pandasでは、リストからデータフレームを生成することもできます。しかし、pandasの操作に慣れていないうちは、ちょっとした操作も難しいですよね。この記事では、①リストからデータフレームを生成、行と列を追加する方法、②インデックス名、コラム名を設定する方法、③DataFrameを転値する方法(行と列が期待と逆だった時の対応)をサンプルコード付きで紹介!
www.yutaka-note.com/entry/pandas_list
 
≫pandas 辞書型からDataFrameを生成|インデックスとコラムの設定も!
≫pandas 辞書型からDataFrameを生成|インデックスとコラムの設定も!
pandasでは、辞書型からデータフレームを生成することもできます。しかし、pandasの操作に慣れていないうちは、ちょっとした操作も難しいですよね。この記事では、①辞書型からデータフレームを生成する方法、②辞書のキーをインデックス名、コラム名それぞれに設定する方法、③DataFrame()とDataFrame.from_dict()の違いをサンプルコード付きで紹介!
www.yutaka-note.com/entry/pandas_dict
 
≫pandas | read_excel() 図解でわかりやすく解説!
≫pandas | read_excel() 図解でわかりやすく解説!
pandasで、excelファイルを読み込むための関数read_excel()について、図解で徹底解説!①表のデータがセルA1から始まっていないときの対応方法②indexやlabelの行や列を指定する方法
www.yutaka-note.com/entry/pandas_read_excel_1
 
≫pandas | read_csv() 図解でわかりやすく解説!
≫pandas | read_csv() 図解でわかりやすく解説!
pandasでcsvファイルを読み込むための関数read_csv()について、図解で徹底解説! ①区切り文字の指定②indexやlabelの行や列を指定する方法③読み込む行・列の指定など細かい設定についての解説記事です!
www.yutaka-note.com/entry/pandas_read_csv
 
≫【レビュー】「Python実践データ分析100本ノック」|100本終えたらpandasが好きになっていた
≫【レビュー】「Python実践データ分析100本ノック」|100本終えたらpandasが好きになっていた
Python実践データ分析100本ノックで、実際に100本終了したレビューです。pythonでのデータ分析の入門書としてかなりの良書だったと思います。・python2~3冊目に何を勉強しようか迷っている人・時間をかけずにデータ分析の基本を学びたい人・pandasへの抵抗を減らしたい人
www.yutaka-note.com/entry/nock_100
 
≫pandas|head(), tail()でデータ先頭, 末尾を抽出!
≫pandas|head(), tail()でデータ先頭, 末尾を抽出!
pandasのDataFrameやSeriesで先頭や末尾の数行をさっと確認したい場合には、head()、tail()メソッドを使用します。引数で表示する行数を指定することもできますので、その使用方法を解説していきます。
www.yutaka-note.com/entry/pandas_head_tail