【问题标题】:Add business days to pandas dataframe with dates and skip over holidays python将工作日添加到带有日期的熊猫数据框并跳过假期python
【发布时间】:2021-02-11 06:58:23
【问题描述】:

我有一个带有日期的数据框,如下表所示。第一个块是它应该看起来的样子,第二个块是我在添加 BDay 时得到的。这是完成后应该是什么样子的示例。我想使用第一列并在日期中添加 5 个工作日,但如果 5 个 Bdays 与假期重叠(例如 21 年 2 月 15 日),那么我需要再添加一天。使用pandas.tseries.offsets import BDay 添加 5Bday 相当简单,但在使用数据框时我不能跳过假期。

我曾尝试使用pandas.tseries.holiday import USFederalHolidayCalendar、workdays 和 workalendar 模块,但无法弄清楚。任何人都知道我能做什么。

正确的例子

DATE EXIT DATE +5
2021/02/09 2021/02/17
2021/02/10 2021/02/18

错误的例子

DATE EXIT DATE +5
2021/02/09 2021/02/16
2021/02/10 2021/02/17

以下是我尝试过的一些代码示例:

import pandas as pd
from workdays import workday
...
df['DATE'] = workday(df['EXIT DATE +5'], days=5, holidays=holidays)

下一个例子:

import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
dt = df['DATE']
df['EXIT DATE +5'] = dt + bday_us

==========================================

最终代码:

下面是我最终确定的代码。由于纽约证券交易所实际交易的日子,我不得不手动定义假期。比如布什总统安息的那一天。

import datetime as dt
import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import BDay

from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday, nearest_workday, \
    USMartinLutherKingJr, USPresidentsDay, GoodFriday, USMemorialDay, \
    USLaborDay, USThanksgivingDay

class USTradingCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('NewYearsDay', month=1, day=1, observance=nearest_workday),
        USMartinLutherKingJr,
        USPresidentsDay,
        GoodFriday,
        USMemorialDay,
        Holiday('USIndependenceDay', month=7, day=4, observance=nearest_workday),
        Holiday('BushDay', year=2018, month=12, day=5),
        USLaborDay,
        USThanksgivingDay,
        Holiday('Christmas', month=12, day=25, observance=nearest_workday)
    ]

offset = 5

df = pd.DataFrame(['2019-10-11', '2019-10-14', '2017-04-13', '2018-11-28', '2021-07-02'], columns=['DATE'])
df['DATE'] = pd.to_datetime(df['DATE'])

def offset_date(start, offset):
  return start + pd.offsets.CustomBusinessDay(n=offset, calendar=USTradingCalendar())

df['END'] = df.apply(lambda x: offset_date(x['DATE'], offset), axis=1)
print(df)

【问题讨论】:

标签: pandas dataframe python-holidays


【解决方案1】:

输入数据

df = pd.DataFrame(['2021-02-09', '2021-02-10', '2021-06-28', '2021-06-29', '2021-07-02'], columns=['DATE'])
df['DATE'] = pd.to_datetime(df['DATE'])

使用 apply 的建议解决方案

from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import BDay

def offset_date(start, offset):
  return start + pd.offsets.CustomBusinessDay(n=offset, calendar=USFederalHolidayCalendar())

offset = 5
df['END'] = df.apply(lambda x: offset_date(x['DATE'], offset), axis=1)

    DATE        END
    2021-02-09  2021-02-17
    2021-02-10  2021-02-18
    2021-06-28  2021-07-06
    2021-06-29  2021-07-07
    2021-07-02  2021-07-12

PS:如果您想使用特定的日历,例如 NYSE,而不是默认的 USFederalHolidayCalendar,我建议您按照 this answer 上的说明创建自定义日历。

我不推荐的替代解决方案

目前,据我所知,pandas 不支持矢量化方法来解决您的问题。但是,如果您想遵循与您提到的方法类似的方法,那么您应该这样做。

首先,您必须定义一个任意遥远的end 日期,其中包括您可能需要的所有时间段,并使用它来创建假期列表。

holidays = USFederalHolidayCalendar().holidays(start='2021-02-09', end='2030-02-09')

然后,通过holidays 参数而不是calendarholidays 列表传递给CustomBusinessDay,以生成所需的偏移量。

offset = 5
bday_us = pd.offsets.CustomBusinessDay(n=offset, holidays=holidays)
df['END'] = df['DATE'] + bday_us

但是,这种方法并不是真正的矢量化解决方案,尽管它看起来很像。请参阅以下SO answer 以获得进一步说明。在引擎盖下,这种方法可能正在执行一种效率不高的转换。这就是它产生以下警告的原因。

PerformanceWarning:将非矢量化 DateOffset 应用于系列 或日期时间索引

【讨论】:

  • 这也有效。如果不申请 df,我很想看看这是否可行。
  • @fthomson 刚刚更新了我的答案。但是回答您的评论,是的,这是可能的,但这可能不是一个好主意。我在回答中提到了阻止您获得所需结果的代码中的小细节。
  • 我实际上也写了一个解决问题的方法。不知道日期偏移量没有被矢量化。 applymap/map 可能会带来轻微的性能提升?
  • @fthomson Mapapply 没有太大区别,它们本质上都经过了一点优化 for 循环。我不认为你会得到比这更好的改进。很高兴您的解决方案有效。但是,如果性能是一个问题,那么仅使用日期范围来获取最后一个元素可能不是最好的方法。根据数据框的大小和日期范围,您可能会为数千行创建相对较大的日期范围,只是为了选择最后一个元素。从这个意义上说,使用带有apply 的日期偏移量将获得更快和更少内存密集型的结果。
  • @davidp13 非常感谢。此外,这个日历问题已在其他问题中得到解决,特别是this one。 PS:我的硕士论文是关于交易领域的,所以我在交易日期方面遇到了很多麻烦 LOL。
【解决方案2】:

这是一种方法

import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from datetime import timedelta as td

def get_exit_date(date):
    holiday_list = cals.holidays(start=date, end=date + td(weeks=2)).tolist()
    # 6 periods since start date is included in set
    n_bdays = pd.bdate_range(start=date, periods=6, freq='C', holidays=holiday_list)
    return n_bdays[-1]

df = pd.read_clipboard()
cals = USFederalHolidayCalendar()
# I would convert this to datetime
df['DATE'] = pd.to_datetime(df['DATE'])
df['EXIT DATE +5'] = df['DATE'].apply(get_exit_date)

这是使用返回日期时间索引的 bdate_range

结果:

    DATE    EXIT DATE +5
0   2021-02-09  2021-02-17
1   2021-02-10  2021-02-18

另一个选项是动态创建假期列表。您也可以只选择一个开始日期并将其留在函数之外,如下所示:

def get_exit_date(date):
    # 6 periods since start date is included in set
    n_bdays = pd.bdate_range(start=date, periods=6, freq='C', holidays=holiday_list)
    return n_bdays[-1]

df = pd.read_clipboard()
cals = USFederalHolidayCalendar()
holiday_list = cals.holidays(start='2021-01-01').tolist()
# I would convert this to datetime
df['DATE'] = pd.to_datetime(df['DATE'])
df['EXIT DATE +5'] = df['DATE'].apply(get_exit_date)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 2020-11-28
    • 2019-08-08
    • 1970-01-01
    • 2017-11-03
    相关资源
    最近更新 更多