如何在Pandas中计算各因子的工作日周均值(含非整周处理)
问题描述
现有一个包含三列的DataFrame,包含A、B、C三个因子,每个因子在指定时间范围内的每个工作日都有对应数值。模拟代码如下:
import pandas as pd import numpy as np index_d = pd.bdate_range(start='10/5/2022', end='10/27/2022') index = np.repeat(index_d,3) values = np.random.randn(3*len(index_d), 1) columns_v = len(index_d)*["A","B","C"] df = pd.DataFrame() df["x"] = np.asarray(index) df["y"] = values df["factor"] = np.asarray([columns_v]).T
需求:绘制三个因子的工作日周均值随时间变化的折线图,工作日周定义为周一至周五。由于起始和结束日期不在整周首尾,第一周均值仅包含10月5日、6日、7日的数据,最后一周同理。周均值对应的时间为该周最后一个工作日(通常为周五,最后一周为周四),最终输出格式需匹配如下结构:
import datetime as dt dt1 = dt.datetime.strptime("20221007", "%Y%m%d").date() dt2 = dt.datetime.strptime("20221014", "%Y%m%d").date() dt3 = dt.datetime.strptime("20221021", "%Y%m%d").date() dt4 = dt.datetime.strptime("20221027", "%Y%m%d").date() d = 3*[dt1, dt2, dt3, dt4] values = np.random.randn(len(d), 1) factors = 4*["A","B","C"] df_output = pd.DataFrame() df_output["time"] = d df_output["values"] = values df_output["factors"] = factors
解决方案
步骤1:数据预处理
先确保日期列是datetime类型,避免分组出错:
df['x'] = pd.to_datetime(df['x'])
步骤2:分组计算周均值
使用groupby结合pd.Grouper按周五结束的周分组,同时计算每个因子的周均值,并保留该周的最后一个工作日作为时间标签:
# 分组计算:按因子+周分组,取y的均值,以及该组的最后日期 df_grouped = df.groupby( ['factor', pd.Grouper(key='x', freq='W-FRI')], dropna=False ).agg( values=('y', 'mean'), # 计算均值,默认跳过NaN time=('x', 'max') # 取该周最后一个实际工作日 ).reset_index(drop=True)
关键细节说明:
pd.Grouper(key='x', freq='W-FRI'):按周五作为周的结束点分组,自动适配非整周的起始/结束数据(比如第一周仅包含10.5-10.7,最后一周包含10.24-10.27)。dropna=False:保留包含NaN的分组(若需剔除可改为dropna=True);mean方法默认跳过NaN值,若需包含NaN计算可改为mean(skipna=False)。time=('x', 'max'):确保每个周均值对应的时间是该组实际存在的最后一个工作日,完美匹配需求。
步骤3:绘制折线图
使用Seaborn的lineplot,通过hue参数区分不同因子:
import seaborn as sns import matplotlib.pyplot as plt sns.lineplot(data=df_grouped, x='time', y='values', hue='factor') plt.xticks(rotation=45) # 旋转日期标签避免重叠 plt.tight_layout() # 自动调整布局 plt.show()
性能与NaN处理补充
- 大数据量适配:
groupby结合pd.Grouper是Pandas原生高效分组方式,无需自定义复杂逻辑,适合大规模数据集。 - NaN处理:若需过滤掉存在NaN的行,可在分组前执行
df = df.dropna(subset=['y']);若需排除整个存在NaN的周,可在分组后执行df_grouped = df_grouped.dropna(subset=['values'])。
内容的提问来源于stack exchange,提问作者swissy
相关产品推荐
相关产品推荐

