基于月末日期滚动计算PERMNO分组下RET最低1%值的滚动均值
问题描述
- 需求:针对每个月末日期和每个PERMNO(公司标识符),计算DataFrame中RET列过去252天内最低1%值的滚动均值。
- 用户尝试代码:
def get_es(x): return (x<=x.iloc[0:249].quantile(0.01)).mean() df['es_1pct'] = df.groupby(['PERMNO'])['RET'].rolling(window = 250, min_periods=200).apply(get_es).reset_index()['RET']
- 存在问题:结果完全错误,返回的是整个样本中低于1%分位数的观测比例,且未实现正确的滚动计算(代码参考自非滚动场景方案)。
- 求助要点:
- 如何仅在月末日期执行滚动计算以提速;
- 如何正确计算最低1%值的滚动均值。
解决方案
1. 仅在月末执行计算(提速)
先标记并筛选出月末日期的行,仅对这些行执行滚动计算,避免无意义的全量计算。假设DataFrame包含date日期列:
import pandas as pd # 转换日期格式并标记月末行 df['date'] = pd.to_datetime(df['date']) df['is_month_end'] = df['date'].dt.is_month_end # 仅保留月末数据用于后续计算 month_end_df = df[df['is_month_end']].copy()
2. 正确计算滚动最低1%值的均值
原代码的核心错误:一是用固定切片而非完整滚动窗口,二是逻辑错误——计算的是比例而非均值。修正逻辑为:在每个滚动窗口内,先找到1%分位数阈值,筛选出窗口内低于等于该阈值的RET值,再计算这些值的均值。
修正后完整代码
def calculate_bottom_1pct_mean(window): # 计算当前滚动窗口的1%分位数 q1_threshold = window.quantile(0.01) # 筛选窗口内低于等于阈值的RET值 bottom_values = window[window <= q1_threshold] # 返回均值,无符合条件值时返回NaN return bottom_values.mean() # 将日期设为索引,使用时间窗口计算 month_end_df = month_end_df.set_index('date') month_end_df['es_1pct'] = month_end_df.groupby('PERMNO')['RET'].rolling( window='252D', # 按过去252天的时间范围计算,若按交易日数量可改为window=252 min_periods=200 # 至少需要200个观测值才执行计算 ).apply(calculate_bottom_1pct_mean, raw=False) # 若需要将结果合并回原DataFrame df = df.merge(month_end_df[['es_1pct']], on=['PERMNO', 'date'], how='left')
关键说明
raw=False确保传入函数的是Series而非numpy数组,方便后续筛选操作;- 时间窗口
window='252D'适用于日期索引场景,能精准匹配过去252天的数据;若数据按交易日排列(一年约252个交易日),可改用固定行数window=252; - 若窗口内无符合条件的数值,函数返回NaN,可根据业务需求调整为0或其他默认值。
内容的提问来源于stack exchange,提问作者DJP
相关产品推荐
相关产品推荐

