如何用Pandas DataFrame.resample实现锚定月度特定日期的滚动均值计算
这个需求确实戳中了Pandas常规滚动/重采样的盲区——因为你要的是锚定每个行日期的月度回溯区间,不是固定天数或者自然月的窗口,常规方法确实搞不定。我之前做类似的需求时,是用自定义区间筛选结合apply(或者批量生成日期对)来实现的,给你分享两种可行的方案:
方案一:逐行生成回溯区间(中小数据量友好)
核心思路是给每一行的日期,循环生成所有符合要求的回溯月度区间,然后筛选区间内的数据计算均值。
步骤拆解:
- 先确保日期列是
datetime类型,并且数据按日期排序:
import pandas as pd from dateutil.relativedelta import relativedelta # 假设你的日期列名为'date',目标值列名为'value' df['date'] = pd.to_datetime(df['date']) df = df.sort_values('date').reset_index(drop=True)
- 定义一个函数,输入单行数据,返回该行所有回溯月度区间的均值:
def get_monthly_roll_means(row, source_df, value_col='value'): current_end = row['date'] mean_dict = {} # 循环生成回溯区间,直到区间起始早于数据最早日期 for n in range(1, 100): # 这里100是最大回溯次数,可按需调整 # 计算区间起始日期:当前结束日期减1个月,再加1天(匹配你的区间规则) interval_start = current_end - relativedelta(months=1) + pd.Timedelta(days=1) if interval_start < source_df['date'].min(): break # 筛选区间内的数据并计算均值 mask = (source_df['date'] >= interval_start) & (source_df['date'] <= current_end) interval_mean = source_df.loc[mask, value_col].mean() # 用回溯次数命名均值列 mean_dict[f'roll_mean_{n}m'] = interval_mean # 更新下一轮的结束日期为当前区间的前一天 current_end = interval_start - pd.Timedelta(days=1) return pd.Series(mean_dict)
- 将函数应用到每一行,合并结果到原DataFrame:
# 生成回溯均值DataFrame dfm = df.apply(lambda row: get_monthly_roll_means(row, df), axis=1) # 合并原数据和均值结果 final_df = pd.concat([df, dfm], axis=1)
方案二:批量生成日期对(大数据量高效)
如果你的DataRow行数很多,逐行apply效率会偏低,可以先批量生成所有需要计算的区间对,再通过分组计算均值:
from itertools import count # 批量生成所有(原行索引, 区间结束日期, 区间起始日期, 均值列名)的组合 date_pairs = [] for idx, row in df.iterrows(): current_end = row['date'] for n in count(1): interval_start = current_end - relativedelta(months=1) + pd.Timedelta(days=1) if interval_start < df['date'].min(): break date_pairs.append({ 'original_idx': idx, 'end_date': current_end, 'start_date': interval_start, 'mean_col': f'roll_mean_{n}m' }) # 转成DataFrame并分组计算均值 pairs_df = pd.DataFrame(date_pairs) mean_results = pairs_df.groupby(['original_idx', 'mean_col']).apply( lambda x: df.loc[(df['date'] >= x['start_date'].iloc[0]) & (df['date'] <= x['end_date'].iloc[0]), 'value'].mean() ).unstack() # 合并回原数据 final_df = df.join(mean_results)
关键注意事项:
- 依赖
python-dateutil库处理月份加减(避免pd.DateOffset在月末日期的异常),需要提前安装:pip install python-dateutil - 如果某个回溯区间内没有数据,均值会返回
NaN,可根据需求用fillna()填充(比如0或前一个均值) - 如果有重复日期,建议先对原数据按日期做聚合(比如取均值),避免重复计算
内容的提问来源于stack exchange,提问作者feetwet
相关产品推荐
相关产品推荐

