基于多条件及前值引用创建分组新变量的R语言问题
分组创建依赖前值的多条件新变量解决方案
问题背景
给定如下数据框:
df <- data.frame( ID = (rep(c(1, 2, 3), times = c(3, 6, 4))), threshold = c(NA, 2, 6, NA, 2, 3, 7, 3, 7, NA, 7, 7, 2) )
需要创建新变量new_var,规则如下:
- 每个ID的首行赋值为1
- 保持当前值,直到
threshold≥5时,new_var加1 - 之后保持新值,直到下一次
threshold≥5时再次加1 - 每个ID的规则独立重置
尝试的代码及错误
使用tidyverse分组后用case_when尝试实现,但报错:
library(tidyverse) df1 <- df %>% group_by(ID) %>% mutate( new_var = NA, #先创建空变量以便后续引用 new_var = case_when( is.na(threshold) == TRUE ~ 1, threshold < 5 ~ new_var[-1], threshold >= 5 ~ new_var[-1] + 1 ) ) %>% ungroup()
错误信息:
Error in
mutate():
! Problem while computingnew_var = case_when(...).
ℹ The error occurred in group 1: ID = 1.
Caused by error incase_when():
!threshold < 5 ~ new_var[-1],threshold >= 5 ~ new_var[-1] + 1must be length 3 or one, not 2.
Backtrace:
- ... %>% ungroup()
- dplyr::case_when(...)
问题根源:new_var[-1]是取整个向量去掉第一个元素,并非引用当前行的前一个值,导致长度不匹配。
解决方案
方法一:用accumulate实现迭代计算
purrr::accumulate()可以逐行基于上一行的结果计算当前值,完美适配依赖前值的场景:
library(tidyverse) df_result <- df %>% group_by(ID) %>% mutate( new_var = accumulate(threshold, .init = 1, function(prev_val, curr_thresh) { if (curr_thresh >= 5) { prev_val + 1 } else { prev_val } }) %>% tail(-1) # 移除.init生成的初始值,匹配原数据行数 ) %>% ungroup()
方法二:用cumsum简化计算
先标记每个ID内的触发点(threshold≥5的行),再计算累积触发次数,最后加上初始值1:
df_result <- df %>% group_by(ID) %>% mutate( trigger = ifelse(threshold >= 5, 1, 0), new_var = 1 + cumsum(trigger) ) %>% ungroup() %>% select(-trigger) # 可选:移除中间变量
最终结果
两种方法得到的结果一致,如下:
| ID | threshold | new_var |
|---|---|---|
| 1 | NA | 1 |
| 1 | 2 | 1 |
| 1 | 6 | 2 |
| 2 | NA | 1 |
| 2 | 2 | 1 |
| 2 | 3 | 1 |
| 2 | 7 | 2 |
| 2 | 3 | 2 |
| 2 | 7 | 3 |
| 3 | NA | 1 |
| 3 | 7 | 2 |
| 3 | 7 | 3 |
| 3 | 2 | 3 |
说明
accumulate方法更直观体现“依赖前值更新”的逻辑,适合复杂迭代规则cumsum方法更简洁,适合规则可转化为累积计数的场景- 两种方法均严格遵循每个ID独立重置的要求
内容的提问来源于stack exchange,提问作者Z K
相关产品推荐
相关产品推荐

