🐼

Pythonのpandasで3営業日後を調べる

2024/06/20に公開

Pythonのpandasで3営業日後を調べる

Pythonで営業日換算するときにpandasの型に営業日換算できる型があったので素振りしました。

なお、個人的にpolarsというpandasよりも高速処理できるライブラリに移行しようとしていますが、polars側には今回紹介する型がないので自作する必要があります。

環境

  • Python
    • 3.12.3
  • holidays
    • 0.50
  • pandas
    • 2.2.2

実装

祝日

日本の祝日を含めて営業日換算する必要があるので、定義する必要があります。

自分で定義してもよいのですが、今回は日本の祝日も取り扱っているライブラリのholidaysを使用します。

次のように呼び出すと、2024年の日本の祝日をset型で返却してくれます。なお、yearsの指定がない場合は返却されないので注意してください。

jp_holidays = holidays.Japan(years=[2024])

注意点としては、三が日に関しては「労働基準法上の扱いは、国民の祝日と同じ法定外休日」らしいのですが、祝日としては制定されていないので明示的に追加しないといけません。

日曜日が祝日だった時の振替休日に関しては、ライブラリ側で定義しています。

営業日

pandasCustomBusinessDayというカスタマイズできる営業日の型があります。今回の記事ではCustomBusinessDayのエイリアスでCDayがあるので、そちらを利用しています。

from pandas.tseries.offsets import CDay

CDayweekmaskを変更することで平日だけでなく土日を営業日として換算できます。また、holidaysに先ほどの祝日をパラメータとして渡すことで、自動的に祝日をスキップしてくれます。

add_b_day = 3
b_day = CDay(n=add_b_day, weekmask="Mon Tue Wed Thu Fri",
        holidays=jp_holidays
      )

当日を含めないで計算する場合

上の計算のままでよいです。

当日を含めて計算する場合

当日を含めて計算する場合は、次のように分類します。

  • 営業日
    • マイナス1(含めるため)
  • 営業日以外(土日・祝日)
    • 加工なし

そのため、次のロジックになります。

base = pd.Timestamp.now() # 基準日

base_is_weekend = base.dayofweek >= 5 # 土・日の判定
base_is_holiday = base in jp_holidays # 祝日の判定
add_b_day = 3 - (0 if (base_is_weekend or base_is_holiday) else 1)

最終コード

テストコードを含めて次のようなコードになります。テストコードで3営業日だと土日を跨がない可能性があるので、10営業日しています。

@pytest.mark.parametrize(
    "year, month, date, expected",
    [
        (2024, 5, 2, "2024-05-20"), # 通常営業日
        (2024, 5, 3, "2024-05-20"), # 3日は祝日
        (2024, 5, 11, "2024-05-24"), # 11日はただの土曜
    ]
)
def test_add_ten_business_date_ignore_base(year, month, date, expected):
    base = pd.Timestamp(year, month, date)

    jp_holidays = holidays.Japan(years=[year])
    add_b_day = 10
    b_day = CDay(n=add_b_day, weekmask="Mon Tue Wed Thu Fri",
                 holidays=jp_holidays
                 )
    assert base + b_day == pd.Timestamp(expected)


@pytest.mark.parametrize(
    "year, month, date, expected",
    [
        (2024, 5, 2, "2024-05-17"), # 通常営業日
        (2024, 5, 3, "2024-05-20"), # 3日は祝日,
        (2024, 5, 11, "2024-05-24"), # 11日はただの土曜
    ]
)
def test_add_ten_business_date_include_base(year, month, date, expected):
    base = pd.Timestamp(year, month, date)

    base_is_weekend = base.dayofweek >= 5 # 土・日の判定
    base_is_holiday = base in jp_holidays
    add_b_day = 10 - (0 if (base_is_weekend or base_is_holiday) else 1)
    b_day = CDay(n=add_b_day, weekmask="Mon Tue Wed Thu Fri",
                 holidays=jp_holidays
                 )
    assert base + b_day == pd.Timestamp(expected)

ソースコード

終わりに

ChatGPTに嘘をつかれたので、かなりこのロジックにたどり着くまでに苦労しました。

個人としてはpandasは新規採用しないと思いますが、今後もpandasを使う現場は多いと思うので、ぜひ役に立てば幸いです。

参考情報

備考

お読みいただきありがとうございます。
役に立った、と思われましたら、ブックマーク・シェア・フォローをしていただければうれしいです。

また、当記事は自分のブログからの転載です。
最新記事は自分のブログに載せていく予定ですのでよろしければ自分のブログもフォローしてください。

Discussion