Pandas 基于闰年的时间序列分解

Pandas time series decomposition based on leap year

我有一个 pandas 时间序列(称为 df),其中有一列(名称为 data)包含5 年时间段内的每日频率数据。以下代码生成一些随机数据:

import pandas as pd
import numpy as np


df_index = pd.date_range('01-01-2012', periods=5 * 365 + 2, freq='D')
df = pd.DataFrame({'data': np.random.rand(len(df_index))}, index=df_index)

我想执行一个简单的年度趋势分解,其中每一天我都减去它一年前的值。此外,我想参加减法中的闰年。有什么优雅的方法可以做到这一点吗?我的方法是用 365 天和 366 天执行差异并将它们分配给新列。

df['diff_365'] = df['data'].diff(365)
df['diff_366'] = df['data'].diff(366)

然后,我对每一行应用一个函数,根据去年的同一日期是 365 天还是 366 天前选择正确的值。

def decide(row):
    if (row.name - 59).is_leap_year:
        return row[1]
    else:
        return row[0]

df['yearly_diff'] = df[['diff_365', 'diff_366']].apply(decide, axis=1)

解释:函数decide将DataFrame中的一行作为参数,该行由列[=52组成=]diff_366(连同 DatetimeIndex)。表达式 row.name returns 行的日期并假设时间序列具有每日频率 (freq = 'D'), 减去 59 天是从 1 月 1 日到 2 月 28 日的天数。根据结果​​日期是否是闰年的一天,返回 diff_366 列中的值,否则返回 [=52 中的值=]列。

这个用了8行,感觉减法一两行就可以了。我试图将类似的函数直接应用于 data 列(通过 apply 并采用默认参数 axis=0).但在这种情况下,我无法考虑我的 DatetimeIndex。有更好的减法吗?

您可能不需要担心明确处理闰年。当你构造一个DatetimeIndex时,你可以指定startend参数。根据 docs:

Of the four parameters start, end, periods, and freq, exactly three must be specified.

这是一个如何重构逻辑的示例:

df_index = pd.date_range(start='01-01-2012', end='12-31-2016', freq='D')

df = pd.DataFrame({'data': np.random.rand(len(df_index))}, index=df_index)

df['yearly_diff'] = df['data'] - (df_index - pd.DateOffset(years=1)).map(df['data'].get)

说明

  • 我们通过提供 startendfreq 参数构造一个 DatetimeIndex 对象。
  • 通过减去 pd.DateOffset(years=1) 从索引中减去 1 年。
  • 使用 pd.Series.map 将这些延迟 1 年的日期映射到 data
  • 从原始 data 系列中减去结果系列。