如何在R中按组实现递减权重的累积求和(Grouped diminishing weighted cumsum)
问题描述
给定如下R语言tibble数据:
df <- tibble( id = c(rep("ID100", 3), rep("ID200", 3), rep("ID450", 3)), year = 2021, month = c(rep(c(5, 6, 7), 3)), value = 5 )
数据预览:
# A tibble: 9 × 4 id year month value <chr> <dbl> <dbl> <dbl> 1 ID100 2021 5 5 2 ID100 2021 6 5 3 ID100 2021 7 5 4 ID200 2021 5 5 5 ID200 2021 6 5 6 ID200 2021 7 5 7 ID450 2021 5 5 8 ID450 2021 6 5 9 ID450 2021 7 5
需求:按id和year分组,对value列计算带递减权重的累积求和(命名为acc_sum)。权重规则为:组内最新的月份(最大month值)对应权重1,上一个月份对应0.5,再上一个对应0.25,以此类推(每次减半)。期望输出如下:
# A tibble: 9 × 5 id year month value acc_sum <chr> <dbl> <dbl> <dbl> <dbl> 1 ID100 2021 5 5 1.25 # 5 * 0.25 2 ID100 2021 6 5 4 # 5 * 0.5 + 1.25 3 ID100 2021 7 5 9 # 5 * 1 + 4 4 ID200 2021 5 5 1.25 5 ID200 2021 6 5 4 6 ID200 2021 7 5 9 7 ID450 2021 5 5 1.25 8 ID450 2021 6 5 4 9 ID450 2021 7 5 9
当前已尝试硬编码权重的实现方式,但扩展性差(组内元素数量变化时需手动修改):
df %>% group_by(id, year) %>% arrange(id, year, month) %>% mutate(weights = last(value) + lag(value) * 0.5 + lag(value, 2) * 0.25)
需要实现无需硬编码权重的通用方案,可借助cumsum或accumulate函数。
解决方案
方法1:用cumsum结合组内位置自动计算权重
这是最简洁的方案,利用组内元素总数和当前行的位置,自动生成对应权重,再通过cumsum计算累积和:
library(dplyr) df %>% group_by(id, year) %>% arrange(month) %>% # 确保按月份升序排列 mutate( # 生成对应权重:组内第i行的权重为0.5^(组内总数 - 当前行号) weight = 0.5^(n() - row_number()), acc_sum = cumsum(value * weight) ) %>% select(-weight) # 移除中间变量
逻辑说明:
n()获取当前组的元素总数row_number()获取当前行在组内的位置(从1开始)- 权重公式
0.5^(n() - row_number())自动适配组内元素数量,比如3个元素的组会生成0.25, 0.5, 1的权重序列,完全匹配需求 cumsum(value * weight)直接计算出期望的累积求和结果
方法2:用purrr::accumulate实现递推累加
如果偏好递推逻辑,可以用accumulate函数,先生成对应权重序列再累加:
library(dplyr) library(purrr) df %>% group_by(id, year) %>% arrange(month) %>% mutate( # 生成反转的权重序列:2^-(0:(n()-1))生成1,0.5,0.25,反转后得到0.25,0.5,1 weights = rev(2^-(0:(n()-1))), acc_sum = accumulate(value * weights, ~ .x + .y) ) %>% select(-weights)
逻辑说明:
2^-(0:(n()-1))生成从1开始依次减半的权重序列,反转后适配按月份升序的行顺序accumulate函数依次累加每个value*weight的值,得到累积求和结果
两种方法都无需硬编码权重,可自动适配任意数量的组内元素,完全满足需求。
内容的提问来源于stack exchange,提问作者HoelR
相关产品推荐
相关产品推荐

