如何实现按递增窗口计算pct_change?适配类似rolling的min_periods功能
问题:自定义pct_change的计算周期(从1逐步递增到指定值)
假设我有如下序列:
import pandas as pd s = pd.Series([90, 91, 85, 95])
当用periods=2计算百分比变化(pct_change)时,得到的结果是:
s.pct_change(periods=2)
输出:
0 NaN 1 NaN 2 -0.055556 3 0.043956 dtype: float64
但如果是多年数据,想用pct_change(365)计算年同比滚动回报,前365个值都会变成NaN,相当于丢失了一年的数据。我希望做个折中:从period=1开始,逐步递增到指定的periods值进行计算,最终得到类似下面的结果(两种形式均可):
第一种结果:
0 NaN 1 0.011111 2 -0.055556 3 0.043956 dtype: float64
第二种结果:
0 0 1 0.011111 2 -0.055556 3 0.043956 dtype: float64
我知道rolling方法有min_periods参数可以实现类似的灵活计算,但pct_change没有对应的参数,请问这种需求是否可行?
解决方案
可行,我们可以通过动态调整每个位置的对比期数来实现,核心思路是:对索引为i的元素,当i < 指定periods时,用period=i计算百分比变化;当i >= 指定periods时,固定用指定的periods计算。
方法一:向量化简洁实现
利用索引的clip方法快速生成每个位置对应的期数,再结合shift和除法模拟pct_change的逻辑(pct_change本质是(当前值 / 前n期值) - 1):
import pandas as pd s = pd.Series([90, 91, 85, 95]) target_periods = 2 # 你需要的目标期数,比如365 # 生成每个位置对应的计算期数:小于target_periods的用索引值,大于等于的用target_periods calc_periods = s.index.clip(upper=target_periods) # 计算百分比变化 result = (s / s.shift(calc_periods)) - 1 # 可选:将第0位的NaN替换为0(对应第二种需求结果) # result = result.fillna(0) print(result)
运行结果(不替换NaN):
0 NaN 1 0.011111 2 -0.055556 3 0.043956 dtype: float64
方法二:numpy条件判断实现
如果需要更直观的逻辑控制,可以用numpy.where分情况处理:
import pandas as pd import numpy as np s = pd.Series([90, 91, 85, 95]) target_periods = 2 # 分情况计算:索引小于target_periods时用索引作为期数,否则用target_periods result = np.where( s.index < target_periods, (s / s.shift(s.index)) - 1, (s / s.shift(target_periods)) - 1 ) # 转换为Series result = pd.Series(result, index=s.index) # 可选:将第0位设为0 # result.iloc[0] = 0 print(result)
补充说明
pct_change没有类似rolling的min_periods参数,是因为它的设计逻辑是固定对比n期前的单一值,而rolling是基于窗口内的聚合计算,两者的核心逻辑不同。我们的自定义方法本质是动态调整对比的基准值位置,正好满足你“逐步递增到目标期数”的需求。
内容的提问来源于stack exchange,提问作者Nermin
相关产品推荐
相关产品推荐

