求助:使用case_when与for循环实现迭代赋值的问题
解决方案:根据日期年份批量整合带后缀的变量列
问题背景
现有数据集包含日期列alldate,以及多个带年份后缀的变量列(如var2000、var2001),需要根据每行alldate的年份,将对应年份的变量值提取到新列中,避免手动编写大量case_when条件。
可复现数据
library(dplyr) library(lubridate) library(stringr) set.seed(123) # 设置随机种子保证结果可复现 dataf <- data.frame(alldate = sample(seq(as.Date('2000/01/01'), as.Date('2005/01/01'), by="day"), 20), var2000 = rnorm(20), var2001 = rnorm(20), var2002 = rnorm(20), var2003 = rnorm(20), var2004 = rnorm(20), var2005 = rnorm(20))
方法1:使用tidyr长表转换(最直观)
先将宽表转成包含年份和变量值的长表,再匹配回原数据:
library(tidyr) # 提取年份列并转长表 var_long <- dataf %>% select(starts_with("var")) %>% pivot_longer(everything(), names_to = "year_col", values_to = "var_value") %>% mutate(year = str_extract(year_col, "\\d{4}") %>% as.integer()) # 匹配回原数据 dataf_result <- dataf %>% mutate(year = year(alldate)) %>% left_join(var_long, by = "year") %>% select(alldate, var_value) %>% rename(var_test = var_value) head(dataf_result)
方法2:动态生成case_when条件(无需转表)
通过字符串拼接生成case_when的所有条件,再解析成表达式执行:
library(rlang) # 获取所有年份对应的变量列 year_vars <- str_subset(names(dataf), "^var\\d{4}") years <- str_extract(year_vars, "\\d{4}") %>% as.integer() # 生成case_when的条件字符串 case_conditions <- map2_chr(years, year_vars, ~ paste0("year(alldate) == ", .x, " ~ ", .y)) case_expr <- paste(case_conditions, collapse = ", ") %>% paste0("case_when(", ., ", TRUE ~ NA_real_)") # 解析并执行表达式 dataf_result <- dataf %>% mutate(var_test = !!parse_expr(case_expr)) head(dataf_result$var_test)
方法3:base R逐行匹配(适合熟悉base R的用户)
利用每行的年份构造对应的列名,再提取值:
dataf$year <- year(dataf$alldate) dataf$var_test <- mapply(function(row_year, row_idx) { col_name <- paste0("var", row_year) dataf[row_idx, col_name] }, dataf$year, seq_len(nrow(dataf))) head(dataf[, c("alldate", "var_test")])
原方法失败原因分析
- 方法a:函数内的for循环未收集所有条件,仅返回最后一次循环的表达式;且
str_c返回的是列名字符串,而非变量引用,case_when无法识别。 - 方法b:循环中每次赋值都会覆盖之前的结果,未定义
yearstart变量,同时用字符串代替了变量引用。 - 方法c:
eval(str_c(...))在ifelse中无法正确识别数据框内的变量环境,导致返回列名字符串而非对应值。
内容的提问来源于stack exchange,提问作者WanderingEpi
相关产品推荐
相关产品推荐

