【问题标题】:Roll back a date offset回滚日期偏移量
【发布时间】:2021-03-20 12:35:21
【问题描述】:

给定一个锚定日期和一个日期偏移量,我如何将偏移量回滚到给定目标日期之前的第一个有效日期?

anchor = pd.to_datetime("2021-03-19")
target = pd.to_datetime("2021-03-04")
offset = pd.DateOffset(weeks=2)
# something like this... resulting in "2021-02-19"
first_before = offset.rollback(anchor, target)
# Want to point out that rollback is functional with most other offsets, such
# as pd.offsets.Week(...).rollback(...). However, rolling back a DateOffset
# returns the same input date (because there is no anchor).

DateOffset 转换为Timedelta 并自行计算。下面进行了简化,以忽略锚定日期出现在目标日期之前的可能性。

first_before = anchor - math.ceil((anchor-target)/pd.Timedelta(weeks=2)) * offset

Timedelta 不会忽略夏令时,因此结果可能会关闭 +- 1 小时。另外DateOffset是唯一一个不能直接转换成Timedelta的偏移量,有各种等效规格,手动转换有点麻烦。

o1 = pd.offsets.DateOffset(weeks=2)
o2 = pd.offsets.DateOffset(days=14)
# Both raise ValueError:
pd.Timedelta(o1)
pd.to_timedelta(o1)
# Both raise AtttributeError:
o1.days
o2.weeks

但最后我觉得肯定有一种我不知道的更简单的内置方法。


edit:展示一种低效的方法。

给定以下日期范围,我想找到小于或等于“2021-03-04”的第一个日期(目标)。

# end is anchor, freq is offset
>>> dr = pd.date_range(freq=offset, end="2021-03-19", periods=20)
>>> dr
DatetimeIndex(['2020-06-26', '2020-07-10', '2020-07-24', '2020-08-07',
               '2020-08-21', '2020-09-04', '2020-09-18', '2020-10-02',
               '2020-10-16', '2020-10-30', '2020-11-13', '2020-11-27',
               '2020-12-11', '2020-12-25', '2021-01-08', '2021-01-22',
               '2021-02-05', '2021-02-19', '2021-03-05', '2021-03-19'],
              dtype='datetime64[ns]', freq='<DateOffset: weeks=2>')
# key is target
>>> dr[dr.get_loc(key="2021-03-04", method="ffill")]
Timestamp('2021-02-19 00:00:00', freq='<DateOffset: weeks=2>')

【问题讨论】:

  • 如果你的锚是2021-03-19,目标日期为2021-03-04,偏移量为2 weeks,为什么预期的答案是2021-02-19而不是2021-02-182021-02-25?你能重新解释一下锚点和目标是如何工作的吗?
  • @user19087 据我所知,pandas 中没有任何内置函数可以为您回滚,但我想这可以通过简单的 for/while 循环并结合回滚来简单地实现里面的逻辑..
  • 如果没有内置函数,我希望直接计算忽略夏令时(即不改变时间)而不是循环(时间效率低)或日期时间索引(空间效率低)。

标签: python pandas date


【解决方案1】:

原来pd.DateOffsetbacked by dateutil.relativedelta。如this answer 中所述,relativedelta 无法转换为timedelta。出于类似的原因,涉及相对增量的绝对计算没有任何意义。如果使用偏移的相对方面,最好的解决方案是在循环中应用偏移,直到满足条件(根据 OP 注释)。

人们可能会假设每个偏移量都与时区无关,而是使用pd.offsets.Day。然而该类是 backed by pandas Timestamp,这就是它可以直接转换为 Timestamp 的原因。唯一可能的解决方案是首先暂时删除任何时区信息:

offset = pd.Timedelta(days=14)
multiplier = math.ceil((anchor.tz_localize(None)-target.tz_localize(None))/offset)
first_before = anchor.tz_localize(None) - multiplier*offset
first_before = first_before.tz_localize(anchor.tz)

您可以看到pd.date_range 采用same approach

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    • 2023-02-05
    • 1970-01-01
    • 2013-03-12
    相关资源
    最近更新 更多