使用rollify计算percent_rank时出现维度不匹配错误的排查
数据结构
我有结构如下的Wide_DF表格(实际行数更多):
| DATE | TICKER | Total_Put |
|---|---|---|
| 2021-01-03 | A | 23.4 |
| 2021-01-03 | B | 13.1 |
| 2021-01-03 | C | 34.6 |
其中TICKER字段包含A、B、C三个取值,每个标的对应全量日期数据,日期按降序排列。
需求
新增第4列,计算Total_Put基于最近60个值的百分位排名。
实现代码
rankCalc <- tibbletime::rollify( ~percent_rank(.x), window = 60) Wide_DF = Wide_DF %>% dplyr::group_by(TICKER) %>% dplyr::mutate(ROW_VALUE = rankCalc(Total_Put )) %>% dplyr::ungroup()
错误信息
Error in
dplyr::mutate():
ℹ In argument:ROW_VALUE=rankCalc(Total_Put ).
ℹ In group 1:TICKER = "A".
Caused by error:
!ROW_VALUEmust be size 1060 or 1, not 60119.
看起来是窗口大小导致的错误,但将percent_rank替换为mean时代码可正常运行,且Wide_DF仅有3000行,请问可能的错误原因是什么?
核心原因
tibbletime::rollify要求传入的函数必须返回单个值,但dplyr::percent_rank()是对整个输入向量计算每个元素的百分位排名,返回的是和输入长度相同的向量,而不是窗口内的单个值——这就导致每次滚动窗口计算时,都会输出60个值,最终叠加后总长度远超分组后的行数,触发维度不匹配错误。
而mean()这类聚合函数是对窗口向量返回单个值,所以能正常运行。
解决方法
需要自定义一个函数,对每个滚动窗口的向量计算当前窗口内最后一个元素的百分位排名,或者直接计算窗口内每个元素的相对排名后取对应位置的值。这里提供两种可行方案:
方案1:自定义窗口内百分位排名函数
# 自定义函数:计算窗口向量中最后一个元素的百分位排名 roll_percent_rank <- function(x) { if (length(x) < 2) return(NA) # 计算窗口内所有值的百分位排名,取最后一个元素的结果 dplyr::percent_rank(x)[length(x)] } # 用rollify包装该函数 rankCalc <- tibbletime::rollify(roll_percent_rank, window = 60) Wide_DF <- Wide_DF %>% dplyr::group_by(TICKER) %>% dplyr::mutate(ROW_VALUE = rankCalc(Total_Put)) %>% dplyr::ungroup()
方案2:使用slider包替代(更灵活)
如果可以安装slider包,它的滚动计算接口更直观,支持自定义窗口逻辑:
library(slider) Wide_DF <- Wide_DF %>% dplyr::group_by(TICKER) %>% dplyr::mutate(ROW_VALUE = slide_dbl( Total_Put, ~dplyr::percent_rank(.x)[length(.x)], .before = 59, # 最近60个值包含当前行,所以向前取59个 .complete = TRUE # 仅当窗口有60个值时计算,否则返回NA )) %>% dplyr::ungroup()
补充说明
因为你的日期是降序排列,slide_dbl的.before=59会取当前行及之前的59行,正好是最近60个值,符合需求;如果需要包含未来的行,可调整.after参数。
内容的提问来源于stack exchange,提问作者Siddharth Somani

