You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Pandas实现时间序列滚动5%分位缩尾后标准化?

解决滚动分位缩尾后标准化的问题

我完全懂你的需求——先对时间序列做滚动窗口的分位缩尾(把超出指定分位的数值截断到对应分位值),再执行滚动标准化,而且之前尝试用rolling.apply没成功对吧?我来一步步帮你搞定这个问题。

第一步:自定义滚动缩尾函数

首先,我们需要写一个自定义函数,处理每个滚动窗口内的缩尾逻辑:对于每个窗口,先计算指定的上下分位值,然后把窗口的最后一个元素(也就是当前行的原始值)截断在这两个分位之间。

import numpy as np
import pandas as pd

def winsorize_single_value(window, lower_q=0.05, upper_q=0.95):
    # 计算窗口内的上下分位值(这里用线性插值,你可以根据需求调整interpolation参数)
    lower_bound = np.percentile(window, lower_q * 100, interpolation='linear')
    upper_bound = np.percentile(window, upper_q * 100, interpolation='linear')
    # 获取当前行的数值(窗口的最后一个元素)
    current_val = window[-1]
    # 执行缩尾:大于上界取上界,小于下界取下界,否则保留原值
    return max(min(current_val, upper_bound), lower_bound)

第二步:应用滚动缩尾到你的数据

接下来,用rolling.apply把这个函数应用到你的序列上。注意设置窗口大小(比如你需要的252天),以及min_periods参数(控制窗口最少需要多少个数据才计算,比如设为1的话,前面不足窗口大小的行也会计算)。

拿你给出的示例数据测试一下(窗口设为10,分位0.15和0.85):

# 示例数据
df = pd.DataFrame({'series1':[78, 1, 3, 4, 5, 6, 7, 8, 99]})
# 应用滚动缩尾
df['winsorized'] = df['series1'].rolling(
    window=10, 
    min_periods=1
).apply(winsorize_single_value, args=(0.15, 0.85))

执行后得到的winsorized列就会符合你预期的缩尾逻辑——每个值都会根据其所在的滚动窗口分位被截断。

第三步:执行滚动标准化

在得到缩尾后的序列后,就可以按照你原来的逻辑执行滚动标准化了:

# 滚动窗口大小保持252
window_size = 252
df['std_series'] = (
    df['winsorized'] - df['winsorized'].rolling(window_size).mean()
) / df['winsorized'].rolling(window_size).std()

注意事项

  • 性能优化:如果你的数据量非常大(比如几十万行),rolling.apply的Python层面循环会比较慢。这时候可以考虑用numba装饰自定义函数来加速,比如在函数上方加@numba.jit(nopython=True)(需要先安装numba)。
  • 分位插值方式:np.percentile的interpolation参数可以调整,比如'nearest'、'lower'等,你可以根据自己的需求选择,这会影响分位值的计算结果。
  • 窗口的包含逻辑:默认的rolling是包含当前行的,如果需要排除当前行(只用过去的252天数据),可以调整窗口的closed参数,比如closed='left'。

内容的提问来源于stack exchange,提问作者Dan Xystus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:33:39