[pandas] 時系列インデックスのDataFrameを、月ごとに切り分ける関数を作ってみた (解説付き)

はじめに

以前から、時系列インデックスのDataFrameを月ごとに切り分けて、それをループさせたいという事が多々ありました。

groupbyメソッド等を使ってできなくもないのですが、そもそもgroupbyは集計を目的としたメソッドということもあり、変にオブジェクト型として持ち回す必要があったりして、どうも実装しにくい。

ということで、勉強も兼ねて実装してみました。

何かの参考にしていただけると嬉しいです。

動作確認ができる今日の結論コード

下の実行ボタンから、動作確認ができます。

コードを変更して実行することもできるので、いじってみて下さい。

解説

関数全体の様子

def get_monthly_df_list(df: pd.DataFrame) -> List[pd.DataFrame]:
    
    # マルチインデックス化
    multi_idx_df = df.set_index([df.index.year, df.index.month, df.index])
    multi_idx_df.index.names = ['year', 'month', '']
    
    # 時系列インデックスの先頭の日付を取得
    start_date = multi_idx_df.index[0][-1]
    end_date = multi_idx_df.index[-1][-1]
    
    df_in_list = []
    target_date = start_date
    while target_date.replace(day=1) <= end_date.replace(day=1):
        
        # 月ごとにdfを切り分けて、リストに格納していく
        monthly_df = multi_idx_df.loc[(target_date.year, target_date.month)]
        df_in_list.append(monthly_df)
        
        # 1ヶ月加算して次のループへ
        target_date += relativedelta(months=1)

    return df_in_list

4行目 : マルチインデックス化

今回、DataFrameを月ごとに切り分ける方法として、マルチインデックスを使いました。

4行目の時点で、DataFrameは以下の状態になっています。

4行目時点でのdf

7行目 : start_dateとend_dateの準備

そして、7行目付近でこのDataFrameの先頭と末尾の日にちを取得しています。

サンプルコードの場合、start_dateには2020-01-31が、end_dateには2020-04-01がそれぞれ代入されています。

13行目 : start_dateから1ヶ月ずつ加算しながらループ

target_dateにstart_dateを代入して、ループ!

毎周、target_dateに1ヶ月ずつ加算していって、end_dateを超えるまでループ!

このループの中で、月ごとにdfを切り分けていって、リストに格納していきます。

応用アイデア

今回は月ごとに切り分ける実装ですが、この時系列マルチインデックスは週単位や四半期単位につけることも可能です。

つまり、週ごとや四半期ごとにDataFrameを切り分けてリスト化するなんてことも簡単にできるかと思います。

必要な方は、是非お試し下さい。

さいごに

今日は、時系列インデックスのDataFrameを月ごとに切り分ける関数を作って、その解説を記事にしました。

何かツッコミがあれば、Twitterなどでご連絡お待ちしております。

最後までありがとうございました。

PON3

PON3

5年くらい前に、30代完全未経験から独学でWeb系のソフトウェアエンジニアになった人。 現在はフリーランスエンジニアとして、大阪の自宅からフルリモートで東京の自社開発企業のお仕事をしている。 主戦場はバックエンドで、Go言語でのAPI開発や、Pythonでのデータ分析が武器。 とは言いつつ、フロントエンドをSPAを実装したり、IaCでクラウドインフラの設計構築したり、スクラム開発でプロジェクト運営したりするなんでも屋。 いつも、ググってきては誰かが書いてくれた記事を見て開発していたが、もらってばかりでなく世の中に返すこともしたいと思い、技術ブログをはじめる。 妻と2歳になる娘の3人暮らし。 何かご用件がある方は、TwitterのDMからどうぞ。