如何为序列末端实现带收缩窗口的pct_change百分比变化计算?
解决分组内收缩窗口的百分比变化计算问题
我明白你的需求:要按label分组计算价格的百分比变化,窗口周期是3,而且当分组序列末端没有足够的后续数据时,要用分组的最后一个价格来计算收缩窗口的变化率,而不是留空NaN。原代码的问题在于pct_change(3)是固定窗口逻辑,当末端没有足够数据时就会生成NaN,且shift(-3)也没处理收缩窗口的场景。
高效的向量化解法
考虑到你数据量较大,我们用向量化操作实现,避免逐元素循环,保证运行效率。核心思路是:对每个分组的价格序列,给每个位置计算对应的目标索引——如果当前位置往后数3个元素存在,就用那个元素;如果不存在,就用分组的最后一个元素。然后直接计算百分比变化。
完整代码如下:
import pandas as pd import numpy as np labels = ['A', 'A', 'A', 'A', 'A', 'B', 'A', 'C', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'B'] prices = [62, 55, 55, 74, 31, 77, 20, 21, 25, 30, 21, 35, 66, 12, 1, 2, 4] df = pd.DataFrame(data={'label': labels, 'price': prices}, columns=['label', 'price']) # 先按label分组排序,同label内保留原索引顺序(和你原代码逻辑一致) df = df.iloc[np.lexsort((df.index, df['label'].values))] def rolling_pct_change(arr, window=3): n = len(arr) # 计算每个位置的目标索引:如果i+window在序列内就用i+window,否则用最后一个元素的索引 target_indices = np.minimum(np.arange(n) + window, n - 1) # 计算百分比变化:(目标价格 / 当前价格) - 1 return (arr.iloc[target_indices].values / arr.values) - 1 # 分组应用自定义函数,生成price_chg列 df['price_chg'] = df.groupby('label')['price'].transform(rolling_pct_change, window=3) print(df)
代码细节解释
- 排序处理:和你原代码逻辑一致,用
np.lexsort把同label的行聚合在一起,同时保留原数据的索引顺序,保证分组内的价格序列符合原始顺序。 - 自定义函数
rolling_pct_change:- 用
np.arange(n)生成每个元素的位置索引,加上窗口大小3得到初始目标位置; - 用
np.minimum把超出序列长度的目标位置替换为最后一个元素的索引(n-1),完美实现收缩窗口的需求; - 全程向量化计算,比循环快几个数量级,适合大数据场景。
- 用
- 分组应用:用
groupby.transform把函数批量应用到每个分组,直接生成对应列,不需要额外的合并操作,代码简洁高效。
验证效果(以A组为例)
A组的价格序列为[62,55,55,74,31,20]:
- 索引3(价格74):往后3个位置超出序列长度,用最后一个价格20计算,变化率为
(20/74)-1≈-0.7297; - 索引4(价格31):同样用最后一个价格20,变化率为
(20/31)-1≈-0.3548; - 索引5(价格20):用自身计算,变化率为0;
这些位置都不会再出现NaN,完全符合你的需求。
内容的提问来源于stack exchange,提问作者eNc
相关产品推荐
相关产品推荐

