🐍

pandasのメソッド以外でグループごとのdataframe操作がしたい

2024/02/20に公開

初めに

pythonでテーブル構造のデータを扱う際に、pandasでグループごとに分けて操作をしたいときがあると思います。そんな時にdf.groupby()によるグループ操作を必ず行うのですが、いろんな記事を見てもgroupyした後に集計とか平均値とか取るだけの記事ばかりだったので、groupe分けしてそのあとにdataframeに対して操作したいんや~~ってなったんで、その時に使った便利なコードをまとめました。

既存のメソッド

pandasのgroupebyオブジェクトには既に便利なメソッドが豊富にあります。
例えば、以下のようなdataframeがあるとします。

id price
x 50
x 50
x 60
y 10
y 30
y 40

このデータフレームに対してid(groupe)ごとの合計値を取得したければ、

df.groupby(["id"]).sum()
# ↓
#    price
# id
# x   160
# y   80

idごとの平均値を取得したければ

df.groupby(["id"]).mean()
# ↓
#    price
# id
# x   53.3333
# y   26.6666

idごとの中央値を取得したければ

df.groupby(["id"]).median()
# ↓
#    price
# id
# x   50
# y   30

などとすでに様々な便利機能が備わっています。

どんな時に?

ではそもそもどんな時に集計とか平均値以外の操作を各Groupeのdfに対して操作を行いたいのでしょうか?
以下のようなデータを考えていきます。

id price created_date
x 50 2024/12/01
x 50 2023/01/01
x 60 2022/05/01
y 10 2023/06/30
y 30 2022/11/01
y 40 2021/01/01

新しいカラムとしてcreated_dateが増えています。このデータが作成された日付だと考えてくれて構いません。
このようにテーブルデータかつ時系列データの構造になっている場合に、
id = x というデータを時系列順に並べて、priceの前回の日付との差分や累積値をもとめ、dfに記憶させたいとします。

from datetime import datetime

df["created_date"] = df["created_date"].apply(lambda x: datetime.strptime(x, "%Y/%m/%d"))
df_groupby = df.groupby(["id"])
all_targets = []
for _, table in df_groupby:
    target = table.sort_values("created_date")
    target["price_cumsum"] = target["price"].cumsum()  #累積値を取得
    target["price_diff"] = target["price"].diff(1)  #前回との差分を取得
    all_targets.append(target)

new_df = pd.concat(all_targets)
# id  price created_date  price_cumsum  price_diff
#2  x     60   2022-05-01            60         NaN
#1  x     50   2023-01-01           110       -10.0
#0  x     50   2024-12-01           160         0.0
#5  y     40   2021-01-01            40         NaN
#4  y     30   2022-11-01            70       -10.0
#3  y     10   2023-06-30            80       -20.0

上記のように、df_groupbyをforループに渡して、それぞれのグループテーブルごとに分けて累積値の取得や前回(時系列的に)との差分を求めることができます。また処理後のグループテーブルをall_targetsのlistに格納して最後にpd.concat(all_targets)で結合することで新しいdfを獲得しています。

まとめ

今回はdf.groupbyの既存のメソッドを用いずにグループごとのdfを操作する方法について記事を書きました。今回のようなカラムの数が小さいdataframeであれば、あまり複雑な操作がないため既存メソッドを使えば問題ないのですが、複雑な条件のもと特定の操作をgroupeごとにしたいなどになると、この操作の方が最終的に可読性も高く操作しやすいコードになったので、まとめとして残しました。(例えば、gropeごとに三年ごとの集計を取りたい、ほかのカラムに条件が増えるなど)

Discussion