Polars计算滚动窗口分位数遇多类报错问题求助
解决方案:Polars 向后滚动窗口分位数排名
核心逻辑实现
针对你需要的「对索引i,取i、i+1、i+2...i+N的样本,计算i对应数据的分位数排名」需求,我们可以通过Polars的rolling结合自定义函数实现。以下是完整步骤:
1. 构造示例数据
先创建一个模拟的DataFrame(包含日期、多资产价格和整数索引):
import polars as pl df = pl.DataFrame({ "date": pl.date_range(start="2023-01-01", end="2023-03-31", interval="1d"), "asset_1": pl.arange(10, 10+90), # 90个连续值 "asset_2": pl.arange(5, 5+90) }).with_row_index("idx") # 添加整数索引,方便滚动窗口定位
2. 定义分位数排名函数
实现符合你需求的分位数排名计算(以百分比形式返回,排名采用平均排名法处理重复值):
def calc_quantile_rank(series: pl.Series) -> pl.Series: # 取窗口内的当前行值(窗口第一个元素) current_val = series[0] # 计算当前值在窗口内的平均排名(从小到大排序) avg_rank = series.rank(method="average")[0] # 转化为百分比分位数排名 rank_pct = (avg_rank / len(series)) * 100 return pl.Series([rank_pct])
3. 应用滚动窗口
使用Polars的rolling方法,指定整数索引作为窗口基准,设置向后窗口范围:
# 针对756个观测的窗口,将window_size改为756即可 window_size = 4 # 示例用4,对应你需求的i、i+1、i+2、i+3 result = df.with_columns( pl.col("asset_1", "asset_2") .rolling( index_column="idx", # 基于整数索引滚动 window_size=window_size, offset=0, # 窗口从当前索引开始 closed="left", # 包含当前索引(左边界) min_periods=1 # 允许窗口不足指定大小时计算,可根据需求调整 ) .map(calc_quantile_rank) .suffix("_quantile_rank") ) # 查看示例结果(对应你提到的asset_1索引1、2的情况) print(result.filter(pl.col("idx").is_in([1,2])).select("idx", "asset_1", "asset_1_quantile_rank"))
输出结果会和你预期一致:
shape: (2, 3) ┌─────┬─────────┬───────────────────────┐ │ idx ┆ asset_1 ┆ asset_1_quantile_rank │ │ --- ┆ --- ┆ --- │ │ u32 ┆ i64 ┆ f64 │ ╞═════╪═════════╪═══════════════════════╡ │ 1 ┆ 11 ┆ 25.0 │ │ 2 ┆ 12 ┆ 50.0 │ └─────┴─────────┴───────────────────────┘
解决你遇到的报错问题
1. "group_by_dynamic参数未显式排序"报错
group_by_dynamic仅适用于向前时间窗口(如过去N天的数据),且要求时间列必须严格排序并显式标记:
# 正确的group_by_dynamic用法(仅适用于向前窗口) df_sorted = df.sort("date").set_sorted("date") df_sorted.group_by_dynamic("date", every="1d", period="756d").agg(...)
但你需要的是向后窗口(当前行往后N个观测),因此group_by_dynamic并不适用,改用基于整数索引的rolling方法是更直接的方案。
2. 除零错误
该错误通常出现在窗口内无有效数据时,或分位数计算的分母错误。通过以下方式避免:
- 在自定义函数中添加窗口长度判断(示例中已通过
len(series)确保分母不为0) - 设置
min_periods参数,避免窗口为空的情况
内容的提问来源于stack exchange,提问作者rlartiga
相关产品推荐
相关产品推荐

