Pythonのpandasで3営業日後を調べる
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])
注意点としては、三が日に関しては「労働基準法上の扱いは、国民の祝日と同じ法定外休日」らしいのですが、祝日としては制定されていないので明示的に追加しないといけません。
日曜日が祝日だった時の振替休日に関しては、ライブラリ側で定義しています。
営業日
pandas
にCustomBusinessDay
というカスタマイズできる営業日の型があります。今回の記事ではCustomBusinessDay
のエイリアスでCDay
があるので、そちらを利用しています。
from pandas.tseries.offsets import CDay
CDay
にweekmask
を変更することで平日だけでなく土日を営業日として換算できます。また、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
を使う現場は多いと思うので、ぜひ役に立てば幸いです。
参考情報
- https://github.com/vacanza/python-holidays
- https://pandas.pydata.org/docs/reference/api/pandas.tseries.offsets.CDay.html
備考
お読みいただきありがとうございます。
役に立った、と思われましたら、ブックマーク・シェア・フォローをしていただければうれしいです。
また、当記事は自分のブログからの転載です。
最新記事は自分のブログに載せていく予定ですのでよろしければ自分のブログもフォローしてください。
Discussion