【问题标题】:Why does DateOffset.rollback() not work the way I expect it to with days / hours?为什么 DateOffset.rollback() 不能像我期望的那样工作几天/几小时?
【发布时间】:2021-11-19 13:23:37
【问题描述】:

我正在尝试将任意输入 pd.Timestamp 移回正确的起始偏移量,除非它已经在偏移量上。

以下代码适用于BusinessMonthEndMonthEndMonthStart 等:

import pandas as pd
from pandas.tseries.offsets import *


def to_start(t: pd.Timestamp, freq: pd.DateOffset) -> pd.Timestamp:
    return freq.rollback(t)


assert to_start(pd.to_datetime("2021-09-27"), MonthEnd()) == pd.to_datetime("2021-08-31")
assert to_start(pd.to_datetime("2021-09-27"), MonthBegin()) == pd.to_datetime("2021-08-31")
assert to_start(pd.to_datetime("2021-08-27"), BMonthEnd()) == pd.to_datetime("2021-07-30")
assert to_start(pd.to_datetime("2021-08-27"), YearBegin()) == pd.to_datetime("2021-01-01")
assert to_start(pd.to_datetime("2021-08-27"), BYearBegin()) == pd.to_datetime("2021-01-01")

# also it works nicely with holiday calendars
from pandas.tseries.holiday import USFederalHolidayCalendar

us_fed_biz_days = CustomBusinessDay(calendar=USFederalHolidayCalendar())
memorial_day = pd.to_datetime("2021-05-31")
the_friday_before_memorial_day = pd.to_datetime("2021-05-28")
assert to_start(memorial_day, us_fed_biz_days) == the_friday_before_memorial_day

但是(这让我发疯)它似乎不适用于DayBusinessDayWeekHour 等:

assert to_start(pd.to_datetime("2021-08-27 05:00"), Day()) == pd.to_datetime("2021-08-27")
assert to_start(pd.to_datetime("2021-08-27 05:00"), BDay()) == pd.to_datetime("2021-08-27")
assert to_start(pd.to_datetime("2021-08-27 05:15"), Hour()) == pd.to_datetime("2021-08-27 05:00")
assert to_start(pd.to_datetime("2021-08-26"), pd.tseries.frequencies.to_offset("W-MON")) == pd.to_datetime("2021-08-24")

我也试过这个:

def to_start(t: pd.Timestamp, freq: pd.DateOffset) -> pd.Timestamp:
        return pd.Period(t, freq=freq).start_time

具有讽刺意味的是,它适用于第二组断言但不适用于第一组?

我对上述断言的期望不是不合理吗?如果是,请问我错过了什么?

【问题讨论】:

  • 如果您查看 source code 中的函数,您会发现在比较中使用哪种偏移量会有所不同 - 在示例的上半部分,您有可变的偏移量(与给定的日期/时间相比),在下部,偏移量是固定的(BDay 介于两者之间)。
  • 我编辑了问题以删除不需要的 is_on_offset() 调用。我还添加了另一个假期用例。

标签: python pandas datetime offset datetimeoffset


【解决方案1】:

从您的示例中,我认为您想使用固定频率(例如一小时)来确定给定的日期/时间,而可变频率(例如业务月末)应该调用 .rollback 如果在 @987654322 中给出检查@ 返回 True(请参阅问题评论部分中链接的源代码)。

例如:

def to_start(t: pd.Timestamp, freq: pd.DateOffset) -> pd.Timestamp:
    try:
        return t.floor(freq) # fixed frequencies should just floor the date/time
    except ValueError: # if freq is variable, we fall into here...
        return freq.rollback(t.floor("D"))

测试:

# variable offsets that depend on the date
assert to_start(pd.to_datetime("2021-09-27"), pd.tseries.offsets.MonthEnd()) == pd.to_datetime("2021-08-31")
assert to_start(pd.to_datetime("2021-09-27"), pd.tseries.offsets.MonthBegin()) == pd.to_datetime("2021-09-01")
assert to_start(pd.to_datetime("2021-08-27"), pd.tseries.offsets.BMonthEnd()) == pd.to_datetime("2021-07-30")
assert to_start(pd.to_datetime("2021-08-27"), pd.tseries.offsets.YearBegin()) == pd.to_datetime("2021-01-01")
assert to_start(pd.to_datetime("2021-08-27"), pd.tseries.offsets.BYearBegin()) == pd.to_datetime("2021-01-01")
assert to_start(pd.to_datetime("2021-08-26"), pd.tseries.frequencies.to_offset("W-MON")) == pd.to_datetime("2021-08-23")
assert to_start(pd.to_datetime("2021-08-28 05:00"), pd.tseries.offsets.BDay()) == pd.to_datetime("2021-08-27")
# fixed offsets (e.g. an hour is always an hour)
assert to_start(pd.to_datetime("2021-08-27 05:00"), pd.tseries.offsets.Day()) == pd.to_datetime("2021-08-27")
assert to_start(pd.to_datetime("2021-08-27 05:15"), pd.tseries.offsets.Hour()) == pd.to_datetime("2021-08-27 05:00")

【讨论】:

  • 我正在查看源代码和您的代码,但同时想指出这不起作用:assert to_start(pd.to_datetime("2021-08-26"), pd.tseries.frequencies.to_offset("W-MON")) == pd.to_datetime("2021-08-24") 因为它错误地返回 2021-08-23 这是一个周日。
  • @ilCatania 你确定吗?根据我的日历,今天是星期一。总的来说,你是对的,我没有为此写一个全面的测试,所以一定要这样做。
  • 你说得对,我看错月份了。好的,这是个好消息!
  • 我已经运行了几个额外的测试,这很有效。感谢您的帮助!
猜你喜欢
  • 2012-02-16
  • 2022-11-12
  • 2010-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-03
  • 2015-09-16
相关资源
最近更新 更多