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
时,你可以指定start
和end
参数。根据 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)
说明
- 我们通过提供
start
、end
和 freq
参数构造一个 DatetimeIndex
对象。
- 通过减去
pd.DateOffset(years=1)
从索引中减去 1 年。
- 使用
pd.Series.map
将这些延迟 1 年的日期映射到 data
。
- 从原始
data
系列中减去结果系列。
我有一个 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
时,你可以指定start
和end
参数。根据 docs:
Of the four parameters
start
,end
,periods
, andfreq
, 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)
说明
- 我们通过提供
start
、end
和freq
参数构造一个DatetimeIndex
对象。 - 通过减去
pd.DateOffset(years=1)
从索引中减去 1 年。 - 使用
pd.Series.map
将这些延迟 1 年的日期映射到data
。 - 从原始
data
系列中减去结果系列。