Pandas で groupby() 関数を使うと,データセットをグループ化して集計できる.さらに Grouper オブジェクトと組み合わせると,より高機能なグループ化を実現できる.今回は groupby() 関数と Grouper オブジェクトを組み合わせて「時系列データの集計」を試す.最後に関連する resample() 関数も試す.
データセット 🪢
今回使うサンプルデータセットを準備する.まず,Pandas の date_range() 関数を使って 2020/1/1 ~ 2020/12/31 の範囲で1年間の DatetimeIndex を作る.そして DatetimeIndex をインデックス値とした DataFrame を作る.今回は count カラムとして 1 ~ 99 の範囲で乱数を含めておく.乱数は Numpy の random.randint() 関数を使う.結果的に以下のような DataFrame になる.
dates = pd.date_range(start='2020-01-01', end='2020-12-31') dates # DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', # '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08', # '2020-01-09', '2020-01-10', # ... # '2020-12-22', '2020-12-23', '2020-12-24', '2020-12-25', # '2020-12-26', '2020-12-27', '2020-12-28', '2020-12-29', # '2020-12-30', '2020-12-31'], # dtype='datetime64[ns]', length=366, freq='D') df = pd.DataFrame(np.random.randint(1, 100, 366), index=dates, columns=['count']) df # 2020-01-01 13 # 2020-01-02 50 # 2020-01-03 44 # 2020-01-04 12 # 2020-01-05 25 # 2020-01-06 82 # 2020-01-07 4 # 2020-01-08 2 # 2020-01-09 27 # 2020-01-10 38 # (中略)

groupby() 関数と Grouper オブジェクトを組み合わせる 🪢
以下のように DataFrame に対して groupby() 関数と Grouper オブジェクトを組み合わせる.まず pd.Grouper(freq='M') と pd.Grouper(freq='Q') を試す.M は「月末集計」で Q は「四半期集計」となる.そして今回は mean() 関数を使って「平均値」を取得する.このように Grouper オブジェクト を使うと簡単に「時系列データの集計」を実現できる.
M: month end frequencyQ: quarter end frequency
# month end frequency df.groupby(pd.Grouper(freq='M')).mean() # 2020-01-31 40.096774 # 2020-02-29 47.655172 # 2020-03-31 46.064516 # 2020-04-30 54.766667 # 2020-05-31 54.354839 # 2020-06-30 49.100000 # 2020-07-31 57.000000 # 2020-08-31 46.387097 # 2020-09-30 41.333333 # 2020-10-31 36.967742 # 2020-11-30 40.266667 # 2020-12-31 57.645161 # quarter end frequency df.groupby(pd.Grouper(freq='Q')).mean() # 2020-03-31 44.538462 # 2020-06-30 52.758242 # 2020-09-30 48.315217 # 2020-12-31 45.010870

また以下のドキュメント(DateOffset objects → Offset aliases と Anchored offsets)を読むと freq に指定できる識別子は他にも多くある.代表的な例を以下に抜粋した.
W: weekly frequencySM: semi-month end frequency (15th and end of month)H: hourly frequencyW-SUN: weekly frequency (Sundays). Same as ‘W’W-MON: weekly frequency (Mondays)W-TUE: weekly frequency (Tuesdays)- etc
例えば pd.Grouper(freq='W-MON') を指定すると「週次集計」で軸にする「曜日(ここでは月曜日)」を指定できる.デフォルトでは W は W-SUN となる.正確に値を計算すると 2020/1/13 (月) の 32.428571 は 2020/1/7 (火) ~ 2020/1/13 (月) の期間での平均値となるため,計算式としては (4 + 2 + 27 + 38 + 23 + 62 + 71) / 7 = 32.428571 として確認できる.
# weekly frequency (Mondays) df.groupby(pd.Grouper(freq='W-MON')).mean() # 2020-01-06 37.666667 # 2020-01-13 32.428571 # 2020-01-20 47.142857 # 2020-01-27 35.714286 # 2020-02-03 37.571429 # 2020-02-10 66.000000 # 2020-02-17 43.285714 # 2020-02-24 48.000000 # (中略)

Grouper オブジェクトの key パラメータ 🪢
デフォルトの挙動では DataFrame のインデックス値が DatetimeIndex や TimedeltaIndex である必要があるため,それ以外の DataFrame だと TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, but got an instance of 'RangeIndex' というエラーが出る.その場合は Grouper オブジェクトの key パラメータを使ってカラムを指定する.
動作確認をするために以下のようなサンプルデータセットを準備する.DataFrame のインデックス値は 0,1,2 ... という数値とし,date カラムに日付を含める.そして pd.Grouper(key='date', freq='M') のように key パラメータを指定すると,期待した平均値となった.
df = pd.DataFrame(
[
{'date': pd.Timestamp('2020-01-01'), 'counter': 1},
{'date': pd.Timestamp('2020-01-02'), 'counter': 2},
{'date': pd.Timestamp('2020-01-03'), 'counter': 3},
{'date': pd.Timestamp('2020-02-01'), 'counter': 4},
{'date': pd.Timestamp('2020-02-02'), 'counter': 5},
{'date': pd.Timestamp('2020-02-03'), 'counter': 6}
]
)
df
# 0 2020-01-01 1
# 1 2020-01-02 2
# 2 2020-01-03 3
# 3 2020-02-01 4
# 4 2020-02-02 5
# 5 2020-02-03 6
df.groupby(pd.Grouper(key='date', freq='M')).mean()
# 2020-01-31 2
# 2020-02-29 5

関連する resample() 関数も試す 🪢
Pandas のドキュメントを読むと,resample() 関数を使った「時系列データの集計」の例も載っていた.最近読んだ「Pandas ライブラリ活用入門」にも resample() 関数の例が載っていた.
- pandas.DataFrame.resample — pandas 1.2.4 documentation
- pandas.core.groupby.DataFrameGroupBy.resample — pandas 1.2.4 documentation
- Group by: split-apply-combine — pandas 1.2.4 documentation
- 作者:Daniel Y. Chen,吉川 邦夫,福島 真太朗
- 発売日: 2019/02/22
- メディア: Kindle版
実際に試したところ,Grouper オブジェクトと同じような集計ができた.例えば,以下は同じデータセットに対して「月末集計」をした結果で,平均値は完全に一致していた.また on パラメータを使ってカラムを指定することもできる.今回試した範囲だと Grouper オブジェクトと resample() 関数の機能面での差は確認できなかった.どう使い分けるんだろう?
df.resample('M').mean() # 2020-01-31 40.096774 # 2020-02-29 47.655172 # 2020-03-31 46.064516 # 2020-04-30 54.766667 # 2020-05-31 54.354839 # 2020-06-30 49.100000 # 2020-07-31 57.000000 # 2020-08-31 46.387097 # 2020-09-30 41.333333 # 2020-10-31 36.967742 # 2020-11-30 40.266667 # 2020-12-31 57.645161 df.resample('Q').mean() df.resample('W-MON').mean() df.resample(on='date', rule='M').mean()

まとめ 🪢
今回は Pandas で groupby() 関数と Grouper オブジェクトを組み合わせて「時系列データの集計」を試した.「月末集計」や「四半期集計」そして「週集計」など高機能なグループ化を実現できる.引き続き気になった機能はどんどん試していくぞー👏