首页 > 解决方案 > Python3 Panda 的假期无法找到过去任意时期的日期

问题描述

制定了我自己对 MLK 日假期的定义,该定义不符合首次观察假期的时间,而是纽约证券交易所首次观察假期的时间。纽约证券交易所于 1998 年 1 月首次观察到 MLK 日。

当询问假期在日期之间发生假期的日子时,它在大多数情况下工作正常,当 MLK 日期不在请求的范围内时返回一个空集,并在它是时返回适当的日期。对于假期之前的日期范围start_date,它适当地返回空集,直到我们到达 1995 年左右,然后它失败了。当空集是正确答案时,我无法弄清楚为什么它会失败,而不是在其他情况下失败。

注意:仍然停留在 Pandas 0.22.0 上。Python3

import pandas as pd
from datetime import datetime
from dateutil.relativedelta import MO
from pandas.tseries.holiday import Holiday

__author__ = 'eb'

mlk_rule = Holiday('MLK Day (NYSE Observed)',
                   start_date=datetime(1998, 1, 1), month=1, day=1,
                   offset=pd.DateOffset(weekday=MO(3)))

start = pd.to_datetime('1999-01-17')
end = pd.to_datetime('1999-05-01')
finish = pd.to_datetime('1980-01-01')
while start > finish:
    print(f"{start} - {end}:")
    try:
        dates = mlk_rule.dates(start, end, return_name=True)
    except Exception as e:
        print("\t****** Fail *******")
        print(f"\t{e}")
        break
    print(f"\t{dates}")
    start = start - pd.DateOffset(years=1)
    end = end - pd.DateOffset(years=1)

运行时,这会导致:

1999-01-17 00:00:00 - 1999-05-01 00:00:00:
    1999-01-18    MLK Day (NYSE Observed)
Freq: 52W-MON, dtype: object
1998-01-17 00:00:00 - 1998-05-01 00:00:00:
    1998-01-19    MLK Day (NYSE Observed)
Freq: 52W-MON, dtype: object
1997-01-17 00:00:00 - 1997-05-01 00:00:00:
    Series([], dtype: object)
1996-01-17 00:00:00 - 1996-05-01 00:00:00:
    Series([], dtype: object)
1995-01-17 00:00:00 - 1995-05-01 00:00:00:
    ****** Fail *******
    Must provide freq argument if no data is supplied

1995 年发生了什么导致它失败,而不是在前几年的同一时期发生?

标签: pythonpandascalendartimestamp

解决方案


解答: 在Holiday类内部,该dates()方法用于收集请求日期范围内的有效假期列表。_reference_dates()为了确保这种情况正常发生,该实现通过内部方法收集了从请求日期范围之前一年到一年之后的所有假期。在此方法中,如果接收Holiday实例有一个内部开始或结束日期,它使用该日期作为要检查的范围的开始或结束,而不是传入的请求范围,即使请求范围中的日期早于或超过规则的开始或结束日期。

现有实现错误地假设可以将必须​​准确识别存在哪些假期的有效范围限制为存在假期的范围。作为日历中一组规则的一部分,Holiday确定假期在哪里不存在与在哪里存在同样重要。NULL 集响应是Holiday该类的一个重要功能。

例如,在需要识别金融市场何时开放或关闭的交易日日历中,日历可能需要准确识别市场在 100 年历史中关闭的日子。市场只在 MLK 日关闭了一小部分历史。包含上述 MLK 假期的日历在询问 MLK start_date[1] 之前时段的开放日或假期时会引发错误。

为了解决这个问题,我在 Holiday 的自定义子类中重新实现了该_reference_dates()方法,以确保当请求的日期范围在假期规则之前start_date或之后扩展时end_date,它使用实际请求的范围来构建参考日期,而不是通过内部开始和结束日期来限制请求。

这是我正在使用的实现。

class MLKHoliday(Holiday):

def __init__(self):
    super().__init__('MLK Day (NYSE Observed)',
                     start_date=datetime(1998, 1, 1), month=1, day=1,
                     offset=pd.DateOffset(weekday=MO(3)))

def _reference_dates(self, start_date, end_date):
    """
    Get reference dates for the holiday.

    Return reference dates for the holiday also returning the year
    prior to the start_date and year following the end_date.  This ensures
    that any offsets to be applied will yield the holidays within
    the passed in dates.
    """
    if self.start_date and start_date and start_date >= self.start_date:
        start_date = self.start_date.tz_localize(start_date.tz)

    if self.end_date and end_date and end_date <= self.end_date:
        end_date = self.end_date.tz_localize(end_date.tz)

    year_offset = pd.DateOffset(years=1)
    reference_start_date = pd.Timestamp(
        datetime(start_date.year - 1, self.month, self.day))

    reference_end_date = pd.Timestamp(
        datetime(end_date.year + 1, self.month, self.day))
    # Don't process unnecessary holidays
    dates = pd.DatetimeIndex(start=reference_start_date,
                             end=reference_end_date,
                             freq=year_offset, tz=start_date.tz)
    return dates

有谁知道这是否已在更新版本的熊猫中得到修复?

[1] 注意:正如在原始问题中构造的那样,mlk_rule实际上不会在 NULLdates()之前的范围内向调用提供 NULL 集,start_date但实际上会在此之前一年左右开始抛出异常。这是因为关于不需要适当的 NULL 集响应的错误假设可以通过将日期范围在每个方向上延长一年来缓解_reference_dates()


推荐阅读