如何在R中用rollapply高效实现带min过滤的滚动均值计算
问题描述
现有如下data.table数据,需按x的唯一值分组处理:
DT <- data.table( N = 1:16, x = c(11,11,11,11,11,11,11,11,21,21,21,21,21,21,21,21), y = rep(1:4,4,4,4,1:4,4,4,4), z = c(53,71,27,64,43,62,61,85,44,56,23,37,31,48,80,38), min = c(2.74,2.77,2.23,2.98,2.25,2.48,2.46,2.22,3.07, 3.08,3.81,3.31,3.32,3.75,3.28,3.04))
处理需求
- 基于
min列和对应行的lower.limit、upper.limit(假设已通过业务逻辑生成)过滤标记行:- 当
N=2时,若当前行min落在前1行的上下限区间内,标记该行; - 当
N=3时,若当前行min落在前2行任意一行的上下限区间内,标记该行; - 当
N=4时,检查前3行; - 当
N≥5时,检查前4行。
- 当
- 对标记出的行,按
x分组计算对应的滚动均值。
当前方法计算成本过高,希望找到rollapply实现方式或更高效的方案。
高效实现方案
步骤1:生成上下限(示例逻辑)
假设上下限为min加减固定值,先添加对应列:
DT[, c("lower.limit", "upper.limit") := .(min - 0.2, min + 0.2), by = x]
步骤2:标记符合条件的行
利用data.table的向量化分组操作,结合shift生成前N行的上下限,用between和any判断区间命中:
DT[, marked := fifelse( N == 1, FALSE, any(between(min, shift(lower.limit, 1:min(N-1,4)), shift(upper.limit, 1:min(N-1,4)))), by = x )]
min(N-1,4)确保只检查最近4行,避免冗余计算。
步骤3:计算标记行的滚动均值
优先使用data.table内置的frollmean(C语言实现,性能远高于rollapply),按x分组计算:
# 以滚动窗口为2为例,可根据需求调整n值 DT[, roll_mean := ifelse(marked, frollmean(z, n=2, align="right", na.rm=TRUE), NA_real_), by = x]
rollapply备选实现
如果必须使用zoo包的rollapply,可按分组处理:
library(zoo) DT[, roll_mean := ifelse(marked, rollapply(z, width=2, FUN=mean, align="right", fill=NA), NA_real_), by = x]
注意:rollapply在大数据量下效率远低于data.table的frollmean,仅适合小数据集场景。
内容的提问来源于stack exchange,提问作者zainul abid
相关产品推荐
相关产品推荐

