Pandas时间序列:基于day-of-week的跨年度pct_change通用方案问询
按星期几匹配前一年首个对应日期的同比百分比变化计算
这需求挺实用的——不是死板地按同月同日同比,而是按星期几对齐前一年的首个对应日期,咱们一步步来实现通用方案。
核心思路拆解
要实现这个需求,关键是给每个日期找到前一年中第一个和它星期几相同的日期,再计算当前值与该日期值的百分比变化。具体分四步:
- 给原数据标记年份和星期几
- 预计算每一年对应的前一年中,每个星期几的首个日期
- 将原数据与预计算的匹配日期关联,获取对应年份的基准值
- 计算同比百分比变化
完整代码实现
1. 生成测试数据
首先用你提供的测试数据初始化:
import pandas as pd import numpy as np # 生成datetime索引的DataFrame idx = pd.date_range('2019-01-01', periods=1000) df = pd.DataFrame(np.arange(1000), index=idx, columns=['value'])
2. 标记年份和星期几
给DataFrame添加辅助列,方便后续分组匹配:
df['year'] = df.index.year df['dow'] = df.index.dayofweek # pandas中0=周一,6=周日
3. 预计算前一年的星期几首个日期
创建一个字典,存储每个年份对应的前一年中,每个星期几的第一个日期:
# 获取所有涉及的年份 years = df['year'].unique() prev_year_dow_mapping = {} for year in years: prev_year = year - 1 # 生成前一年的完整日期范围 prev_year_dates = pd.date_range(start=f"{prev_year}-01-01", end=f"{prev_year}-12-31") # 按星期几分组,取每个组的第一个日期 prev_year_dow_mapping[year] = ( prev_year_dates.to_series(name="match_date") .groupby(prev_year_dates.dayofweek) .first() )
4. 关联匹配日期并计算同比
这里推荐用merge的方式(比apply高效得多,适合大数据量):
# 把预计算的映射转换成DataFrame,方便合并 prev_year_dows_df = [] for year, dow_map in prev_year_dow_mapping.items(): temp_df = dow_map.reset_index().rename(columns={"index": "dow"}) temp_df['year'] = year prev_year_dows_df.append(temp_df) prev_year_dows_df = pd.concat(prev_year_dows_df) # 合并原数据与匹配日期 df_merged = df.merge(prev_year_dows_df, on=['year', 'dow'], how='left') # 通过匹配日期获取前一年的基准值 df_merged['prev_year_value'] = df_merged['match_date'].map(df['value']) # 计算同比百分比变化 df_merged['pct_change'] = (df_merged['value'] - df_merged['prev_year_value']) / df_merged['prev_year_value']
验证你的例子
拿你提到的2020-01-01来验证:
- 2020-01-01是周三(
dow=2),2019年第一个周三是2019-01-02 - 2020-01-01的
value是365(因为2019年有365天,从0开始计数),2019-01-02的value是1 - 计算得到的
pct_change为(365-1)/1 = 364,也就是36400%,完全符合你的预期
通用场景适配
这个方案自动处理平年/闰年的差异,不管目标年份是平年还是闰年,都会准确找到前一年中对应星期几的首个日期,适用于任意连续年份的时间序列数据。
内容的提问来源于stack exchange,提问作者FooBar




