基于日期动态窗口的Pandas DataFrame滚动均值计算方案问询
基于日期动态窗口的Pandas滚动均值计算方案
一、基础需求:按日期范围确定动态窗口
针对"取当前行日期前3-8天数据计算均值"的需求,无需暴力逐行循环,可通过Numpy广播或Pandas交叉合并实现高效计算。
步骤与代码示例
- 准备样本数据并统一字段格式:
import pandas as pd data = [ [1, 'C', 'A', 870], [2, 'D', 'A', 920], [3, 'C', 'A', 120], [4, 'D', 'A', 120], [6, 'C', 'A', 120], [8, 'D', 'A', 1200], [8, 'c', 'A', 720], [10, 'D', 'A', 480], [11, 'D', 'A', 600], [12, 'C', 'A', 720], [13, 'D', 'A', 80], [13, 'D', 'A', 600], [14, 'D', 'A', 1200], [18, 'E', 'B', 150] ] df = pd.DataFrame(data, columns=['dates', 'material', 'location', 'quantity']) # 统一material大小写,避免大小写匹配问题 df['material'] = df['material'].str.upper()
- 用Numpy广播实现高效计算:
dates = df['dates'].values # 生成布尔掩码:标记每行对应的符合日期范围的历史行 mask = (dates[:, None] - dates[None, :] >= 3) & (dates[:, None] - dates[None, :] <= 8) # 基于掩码计算每行的均值 df['Mean'] = [df['quantity'][mask[i]].mean() if mask[i].any() else pd.NA for i in range(len(df))]
此方法利用Numpy的向量运算特性,比逐行循环效率提升显著,适合中等及以上数据量场景。
二、扩展需求:结合material字段过滤窗口
若要求窗口内数据必须与当前行material值一致,只需在上述掩码基础上添加material匹配条件:
代码示例
materials = df['material'].values # 同时满足日期范围和material相同的掩码 mask_material = mask & (materials[:, None] == materials[None, :]) # 计算带material过滤的均值 df['Mean_material'] = [df['quantity'][mask_material[i]].mean() if mask_material[i].any() else pd.NA for i in range(len(df))]
备选:用Pandas交叉合并实现(更直观)
如果对性能要求不高,交叉合并的方式更易理解:
# 交叉合并原表与自身,获取所有行对 df_merge = df.merge(df, how='cross', suffixes=('', '_hist')) # 筛选符合日期范围和material匹配的行 df_merge = df_merge[ (df_merge['dates'] - df_merge['dates_hist'] >= 3) & (df_merge['dates'] - df_merge['dates_hist'] <= 8) & (df_merge['material'] == df_merge['material_hist']) ] # 分组计算均值并合并回原表 mean_result = df_merge.groupby(['dates', 'material', 'location', 'quantity'])['quantity_hist'].mean().reset_index(name='Mean') df = df.merge(mean_result, on=['dates', 'material', 'location', 'quantity'], how='left')
结果验证
两种方法均可得到与预期一致的计算结果,例如:
- 日期10的D类数据,均值为(920+120)/2=520
- 日期18的E类数据,因无匹配的历史数据,均值为
pd.NA
内容的提问来源于stack exchange,提问作者zjsuper
相关产品推荐
相关产品推荐

