【Python】pandas.Grouperで時系列データを楽々groupby!

f:id:hesma2:20210121220126p:plain

時系列データを日次・週次・月次(daily, weekly, monthly)でそれぞれ集計・グルーピングするのに便利なpandas.Grouperを紹介します!

動作環境

MacOS Catalina 10.15.7
Python 3.8.2
pandas 1.2.1
VSCode 1.52.1

VSCode拡張機能のJupyterを使用します。 詳細はこちらを参照ください。

dev.classmethod.jp

pandas.Grouper

pandas.pydata.org

データ準備

2020年のa店舗、b店舗、c店舗の日次来客数のデータを使用します。

import pandas as pd

store_visits_df = pd.read_csv("./csv/store_visits_2020.csv")

このようなデータになっています。

pandas.Grouperを使用する際の注意点として、日付データのカラムは datetime型にしておく必要があります!

store_visits_df.dtypes
date         object
store_id     object
visit_num     int64
dtype: object

dateカラムはobject型になっていると以下のエラーが発生します。

TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, but got an instance of 'Index'

以下のようにしてdatetime型に変換しておきましょう。

store_visits_df["date"] = pd.to_datetime(store_visits_df["date"])

日次の集計(daily)

pandas.Grouperの使用例として、まずは日次の集計をしていきます。

日次の来客合計数

store_visits_df.groupby(pd.Grouper(key="date", freq="D")).sum()

 

■オプション解説

key: groupbyするカラム名

freq: 集計する単位(datetime型のkeyを指定した場合のみ)

 

インデックスカラムを対象にgroupbyしたい場合は、 level オプションでインデックス名、または数字でレベルを指定可能です。

(
    store_visits_df
    .set_index(["store_id", "date"])
    .groupby(pd.Grouper(level="date", freq="D"))
    .sum()
)

 

■オプション解説

label: groupbyするインデックス名、もしくはレベル(0始まり)

 

週次の集計(weekly)

freq="W"を指定するだけです!

store_visits_df.groupby(pd.Grouper(key="date", freq="W")).sum()

2020-01-01は水曜日。
だが週次の集計データの最初は2020-01-05(日)になっています。
どういうことでしょう?
試しに2020-01-05のグルーピングの中身をみてみます。

store_visits_df.groupby(
    pd.Grouper(key="date", freq="W")
).get_group("2020-01-05")

実は、
2020-01-01(水) ~ 2020-01-05(日) の合計が、2020-01-05(日)表記で集計されています。
次の週では、
2020-01-06(月) ~ 2020-01-12(日) の合計が、2020-01-12(日)表記です。

つまり、 月曜から日曜の集計を、日曜で表示しているのです。

f:id:hesma2:20210121234019p:plain

例えば、月曜 ~ 日曜の集計で、週始まりの月曜の日付を表示させたい場合は、以下のようにして集計できます。

store_visits_df.groupby(
    pd.Grouper(key="date", freq="W-MON", closed="left", label="left")
).sum()

 

■オプション解説

freq="W-MON": 月曜基準の週 freq="W": "W-SUN"と同義。日曜基準の週

closed="left": 基準を左端にするか、右端にするか(デフォルトは"right" )

label="left": 基準の右側にある基準日を表記するか、左側にある基準日を表記するか

これらの関係性に関しては、以下の記事がわかりやすく紹介してくれています。(resampleに関する記事だが、freq, closed, labelに関しては同じ挙動をします。)

note.com</chttps://blog.hatena.ne.jp/hesma2/hesma2.hatenablog.com/edit?entry=26006613681427048#previewite>

f:id:hesma2:20210122001044p:plain
https://note.com/yokkai/n/nda684b0307d5 から引用

 

月次の集計(monthly)

store_visits_df.groupby(pd.Grouper(key="date", freq="M")).sum()

 

■オプション解説

freq="M": 月次(月末日が基準)

 

月初日で表記させたい場合は、以下のようになります。

store_visits_df.groupby(pd.Grouper(key="date", freq="MS")).sum()

 

■オプション解説

freq="MS": 月次(月初日が基準)

 

明示的にclosed, labelで指定も可能です。(環境によってはデフォルトのままclosed="right", label="right" になっている可能性もあります。)

store_visits_df.groupby(
    pd.Grouper(key="date", freq="MS", closed="left", label="left")
).sum()

まとめ

時系列データを楽々groupbyするpandas.Grouperを紹介しました!

日次集計

store_visits_df.groupby(pd.Grouper(key="date", freq="D")).sum()

週次集計(月曜始まり)

store_visits_df.groupby(
    pd.Grouper(key="date", freq="W-MON", closed="left", label="left")
).sum()

月次集計(月初日)

store_visits_df.groupby(pd.Grouper(key="date", freq="MS")).sum()

pandas.Grouperと同じような処理が可能な、
resampleとpandas.date_rangeで処理を比較してみました ↓

hesma2.hatenablog.com