R按keyword分组计算4/8/12周滚动中位数 循环及filter方法失效
解决方案
核心问题梳理
你原来的代码存在3个核心问题:
- dplyr的
group_by仅对dplyr内置的函数生效,不会修改全局行索引,for循环遍历全表行号时不会按keyword分组计算,会跨关键词取前序行的hits值,结果自然错误 - 直接对整列执行
hits[pid-1:pid-8]这类切片是向量化操作,不会逐行计算对应窗口的数值,仅会返回固定的切片结果 - 你原来的for循环里将数值型的行号i和字符串类型的"9"/"5"/"13"比较,会触发隐式类型转换,也是常见的错误点
- rollmedian本身支持偶数窗口,你也可以使用适配性更强的滚动计算函数实现需求
推荐实现方案
方案1:使用zoo包rollapply(兼容性最好)
该方案支持任意窗口大小,自动按分组计算,不需要手写for循环:
# 加载依赖包 library(dplyr) library(zoo) summary3 <- summary2 %>% # 按关键词分组后按日期升序排列,*必须确保时序正确否则窗口计算会出错* group_by(keyword) %>% arrange(date, .by_group = TRUE) %>% mutate( # align = "right"表示窗口右边界对齐当前行,取当前行+前N-1行构成窗口,符合「过去N周」的计算逻辑 # fill = NA表示窗口长度不足时填充NA,你可以按需调整填充值 median4 = rollapply(hits, width = 4, FUN = median, align = "right", fill = NA), median8 = rollapply(hits, width = 8, FUN = median, align = "right", fill = NA), median12 = rollapply(hits, width = 12, FUN = median, align = "right", fill = NA) ) %>% ungroup()
如果你需要和原有逻辑一致,前N-1行填充行号而非NA,可以补充下面的逻辑:
summary3 <- summary3 %>% group_by(keyword) %>% mutate(pid = row_number(), median4 = ifelse(pid < 4, pid, median4), median8 = ifelse(pid < 8, pid, median8), median12 = ifelse(pid < 12, pid, median12)) %>% ungroup()
方案2:使用tidyverse生态的slider包(语法更直观)
slider包是tidyverse官方推荐的滚动计算工具,参数更易懂:
library(dplyr) library(slider) summary3 <- summary2 %>% group_by(keyword) %>% arrange(date, .by_group = TRUE) %>% mutate( # .before = N 表示取当前行往前N行,加当前行共N+1行构成窗口 # .complete = TRUE 表示窗口长度不足时返回NA median4 = slide_dbl(hits, median, .before = 3, .complete = TRUE), median8 = slide_dbl(hits, median, .before = 7, .complete = TRUE), median12 = slide_dbl(hits, median, .before = 11, .complete = TRUE) ) %>% ungroup()
内容的提问来源于stack exchange,提问作者jimjames
相关产品推荐
相关产品推荐

