如何在Pandas中生成含工作日逻辑的指定日月度日期范围?
解决Pandas中生成考虑工作日的月度固定日期范围问题
没错,Pandas自带的偏移别名大多围绕月初、月末这类节点,确实没有直接支持每月固定日期(比如20号)还能自动调整到工作日的现成方法。不过咱们可以拆成两步来搞定:先生成每月固定日的基础序列,再把非工作日调整成合规的工作日。下面分正向、反向两个场景给你详细说明:
正向场景:从指定日期开始,生成每月固定日的工作日日期
需求:从2017-12-20开始,每月按20日生成日期(共5个),若当日非工作日则顺延至下一个工作日,后续仍以20日为基准。
步骤1:生成每月20号的基础日期序列
咱们可以先构造出每月20号的原始日期,这里用MS(月初)偏移量加上19天,就能精准定位到每个月的20号:
import pandas as pd import numpy as np # 生成基础的每月20号日期 base_dates = pd.date_range(start='2017-12-20', periods=5, freq='MS') + pd.DateOffset(days=19) # 输出示例:DatetimeIndex(['2017-12-20', '2018-01-20', '2018-02-20', '2018-03-20', '2018-04-20'], dtype='datetime64[ns]', freq=None)
步骤2:调整为下一个工作日
用np.busday_offset函数,设置roll='forward'就能实现「非工作日顺延至下一个工作日」的逻辑:
# 转换为datetime64格式后调整工作日 adjusted_dates = np.busday_offset(base_dates.astype('datetime64[D]'), 0, roll='forward') # 转回Pandas DatetimeIndex格式 adjusted_dt_range = pd.DatetimeIndex(adjusted_dates) print(adjusted_dt_range)
举个例子,如果2018-02-20是周二(工作日)就直接保留;要是当天是周六,就会自动调整为2018-02-22(下周一)。
反向场景:以指定日期为终点,倒推生成每月固定日的工作日日期
需求:以2018-05-20为终点,倒推生成5个每月20号的日期,若当日非工作日则倒推至上一个工作日。
步骤1:生成倒推的每月20号基础日期
用date_range的end参数结合-MS(倒推月初)偏移量,就能生成倒序的每月20号基础日期:
# 倒推生成基础的每月20号日期 base_dates_reverse = pd.date_range(end='2018-05-20', periods=5, freq='-MS') + pd.DateOffset(days=19) # 输出示例:DatetimeIndex(['2018-05-20', '2018-04-20', '2018-03-20', '2018-02-20', '2018-01-20'], dtype='datetime64[ns]', freq=None)
步骤2:调整为上一个工作日
这次把roll参数设为'backward',就能实现「非工作日倒推至上一个工作日」的逻辑:
adjusted_dates_reverse = np.busday_offset(base_dates_reverse.astype('datetime64[D]'), 0, roll='backward') adjusted_dt_range_reverse = pd.DatetimeIndex(adjusted_dates_reverse) print(adjusted_dt_range_reverse)
比如如果2018-01-20是周六,就会自动调整为2018-01-19(周五)。
额外优化:封装成可复用函数
如果需要多次使用这个逻辑,可以把它封装成一个函数,方便随时调用:
def get_monthly_business_dates(start=None, end=None, periods=None, day_of_month=20, roll_direction='forward'): # 生成基础日期序列 if start: base_dates = pd.date_range(start=start, periods=periods, freq='MS') + pd.DateOffset(days=day_of_month-1) elif end: base_dates = pd.date_range(end=end, periods=periods, freq='-MS') + pd.DateOffset(days=day_of_month-1) else: raise ValueError("必须提供'start'或'end'参数中的一个") # 调整为工作日 adjusted_dates = np.busday_offset(base_dates.astype('datetime64[D]'), 0, roll=roll_direction) return pd.DatetimeIndex(adjusted_dates) # 正向场景调用 forward_dates = get_monthly_business_dates(start='2017-12-20', periods=5, day_of_month=20) # 反向场景调用 reverse_dates = get_monthly_business_dates(end='2018-05-20', periods=5, day_of_month=20, roll_direction='backward')
这样不管你要生成哪个固定日期的月度工作日序列,都能轻松搞定啦。
内容的提问来源于stack exchange,提问作者Jaco
相关产品推荐
相关产品推荐

