Python如何高效实现带不等值匹配条件的SAS SQL两表合并
Python高效实现滚动11个月累计收益计算方案
你原代码的性能瓶颈来自按id全量内连接生成的笛卡尔积临时数据,同id下如果有m条月度记录,单id就会生成m²条临时行,数据量稍大就会直接爆内存。该需求本质是按id分组的滑动窗口累计收益计算,无需走不等值join逻辑,用pandas原生rolling方法即可高效实现,性能与SAS实现接近。
适用场景:数据为严格月度频率,每个id每月仅对应1条收益记录
实现代码
import pandas as pd import numpy as np # 第一步:数据预处理,筛选范围+排序 indst = indst.loc[indst['DATE'].dt.year >= 1960, ['id', 'DATE', 'RET']].sort_values(by=['id', 'DATE'], ignore_index=True) # 计算对数毛收益 indst['log_gross_ret'] = np.log(1 + indst['RET']) # 第二步:按id分组计算滑动窗口累计收益 def calc_11month_ret(group): # shift(1)取当前行之前的记录,滚动窗口取近11条,要求满11条才计算 group['sum_log'] = group['log_gross_ret'].shift(1).rolling(window=11, min_periods=11).sum() # 筛选1961年及以后的记录,对应原SAS中a表的过滤条件 group = group.loc[group['DATE'].dt.year >= 1961, ['id', 'DATE', 'sum_log']].copy() # 转换为累计收益率 group['ret11'] = np.exp(group['sum_log']) - 1 return group.drop(columns='sum_log') ret11 = indst.groupby('id', group_keys=False).apply(calc_11month_ret).sort_values(by=['DATE', 'id']).reset_index(drop=True)
该方案时间复杂度为O(n),空间复杂度为O(n),64GB内存可轻松处理千万级记录,不会出现内存不足问题。
补充:若数据存在月度缺失的预处理步骤
如果同一id下存在月份断档,需要先补全连续月度序列,保证窗口计算逻辑正确:
# 将日期转为月度周期 indst['month'] = indst['DATE'].dt.to_period('M') # 按id分组后重索引,补全所有连续月份 indst = indst.set_index('month').groupby('id', group_keys=False).apply( lambda x: x.reindex(pd.period_range(x.index.min(), x.index.max(), freq='M')) ).drop(columns=['id']).reset_index() # 补全DATE字段,可根据你原数据的日期规则选择月初/月末 indst['DATE'] = indst['month'].dt.to_timestamp(how='end')
内容的提问来源于stack exchange,提问作者user16746453
相关产品推荐
相关产品推荐

