使用group_by结合dplyr::do()实现序列增量计算时遇到问题
用dplyr实现分组增量计算的解决方案
嘿,我懂你遇到的痛点了——用for循环能轻松搞定单组的增量计算,但放到多分组(比如多个id)场景下,想结合group_by和dplyr::do()实现就卡壳了对吧?其实现在dplyr有更现代、简洁的写法,而且do()已经不是官方推荐的用法了,我给你分享几个实用的方案:
首先先明确你的需求:从初始值5开始,每一步累加2*obs1(你的for循环里写的是df$obs1[i] + df$obs1[i],也就是两倍的obs1),最终得到包含初始值和各年份更新值的序列。下面我用扩展后的多id数据来模拟实际场景,这样你能直接套用。
方法一:用purrr::accumulate + 分组(推荐)
purrr的accumulate函数天生适合处理这种累积计算,配合dplyr的分组操作非常丝滑:
先加载所需的包:
library(dplyr) library(purrr)
准备示例数据(加入第二个id来模拟多分组):
df <- data.frame( year = rep(2000:2002, 2), id = rep(letters[1:2], each = 3), obs1 = rep(1, 6), obs2 = rep(1, 6) ) initial_value <- 5
实现分组增量计算:
# 得到每个年份对应的更新后的值(不含初始值) result <- df %>% group_by(id) %>% mutate( cumulative_value = accumulate(2 * obs1, ~ .x + .y, .init = initial_value) %>% tail(n()) ) %>% ungroup()
如果想把初始值也作为每个组的第一行,可以调整成这样:
# 包含初始值的完整序列 result_with_initial <- df %>% group_by(id) %>% summarise( year = c(NA, year), # NA标记初始值对应的年份 obs1 = c(NA, obs1), cumulative_value = accumulate(2 * obs1, ~ .x + .y, .init = initial_value) ) %>% ungroup()
方法二:用base R的cumsum(无需额外包)
如果你不想加载purrr,用base R的cumsum也能搞定:
result_cumsum <- df %>% group_by(id) %>% mutate( cumulative_value = initial_value + cumsum(2 * obs1) ) %>% # 追加每个组的初始值行 bind_rows( df %>% distinct(id) %>% mutate(year = NA, obs1 = NA, cumulative_value = initial_value) ) %>% arrange(id, year) %>% ungroup()
为啥不推荐用do()?
虽然do()也能实现需求,但语法繁琐,可读性差,而且现在dplyr官方更推荐用group_modify、summarise或者向量操作来替代。给你看看用do()的写法(仅作参考,不推荐):
# 不推荐的do()写法 result_do <- df %>% group_by(id) %>% do({ increments <- 2 * .$obs1 vals <- accumulate(increments, ~ .x + .y, .init = initial_value) data.frame(., cumulative_value = tail(vals, n())) }) %>% ungroup()
对比下来,前面两种方法是不是清爽多了?运行上面的代码,就能得到和你for循环完全一致的结果,而且能轻松处理多分组的实际场景。
内容的提问来源于stack exchange,提问作者hermo
相关产品推荐
相关产品推荐

