如何基于变量名动态创建case_when()表达式实现批量年份匹配?
批量匹配收入变量与对应年份销售变量的高效实现
问题背景
现有包含多组收入变量(revenues_*)和销售变量(sales_*)的数据框,需要为每个收入变量生成对应的年份变量:当收入变量的值与某年份的销售变量值匹配时,返回该年份,否则返回NA。手动逐个编写case_when逻辑扩展性极差,需要高效的批量处理方案。
示例数据与预期效果
以下是简化示例及手动实现的预期逻辑(仅作效果参考,扩展性差):
library(tidyverse) library(rlang) # 创建示例数据框 df <- tibble( revenues_1 = c(1, 3, 4), revenues_2 = c(2, 4, 5), sales_2005 = c(1, 2, 3), sales_2006 = c(2, 3, 4), sales_2007 = c(3, 4, 5) ) # 手动实现逻辑(仅作效果参考) # df <- df %>% # rowwise() %>% # mutate( # year_1 = case_when( # revenues_1 == sales_2005 ~ 2005, # revenues_1 == sales_2006 ~ 2006, # revenues_1 == sales_2007 ~ 2007, # .default = NA # ), # year_2 = case_when( # revenues_2 == sales_2005 ~ 2005, # revenues_2 == sales_2006 ~ 2006, # revenues_2 == sales_2007 ~ 2007, # .default = NA # ) # ) %>% # ungroup()
无效尝试分析
你尝试的代码未生效的核心问题:生成的表达式中var == 'sales_2005'是将收入变量的值与**字符串sales_2005**比较,而非与数据框中sales_2005列的数值比较,逻辑完全错误。
# 你的无效尝试代码 # years_exprs <- map(2005:2007, ~parse_expr(paste0("var == 'sales_", .x, "' ~ ", .x))) # # df <- df %>% # rowwise() %>% # mutate(across(starts_with("revenues_"), function(var) { # case_when( # !!!years_exprs, # .default = NA # ) # }, .names = "year_{.col}")) %>% # ungroup()
两种高效批量实现方案
方案1:动态生成case_when表达式(宽格式处理)
通过rlang动态生成每个收入变量对应的匹配条件,无需转换数据格式,代码简洁高效:
# 提取销售列及对应年份 sales_cols <- str_subset(names(df), "^sales_") years <- str_extract(sales_cols, "\\d+") %>% as.integer() # 定义函数:为指定收入变量生成case_when表达式 generate_case_when <- function(rev_col) { # 逐个生成"收入列 == 销售列 ~ 年份"的条件表达式 conditions <- map2(sales_cols, years, ~expr(!!sym(rev_col) == !!sym(.x) ~ .y)) # 组合成完整的case_when表达式 expr(case_when(!!!conditions, .default = NA)) } # 批量处理所有收入变量 df_result <- df %>% mutate(across( starts_with("revenues_"), ~eval_tidy(generate_case_when(cur_column())), # 重命名新变量:将revenues_X转为year_X .names = "year_{str_remove(.col, 'revenues_')}" )) # 查看结果 df_result
运行后输出:
# A tibble: 3 × 7 revenues_1 revenues_2 sales_2005 sales_2006 sales_2007 year_1 year_2 <dbl> <dbl> <dbl> <dbl> <dbl> <int> <int> 1 1 2 1 2 3 2005 2006 2 3 4 2 3 4 2006 2007 3 4 5 3 4 5 2007 2007
方案2:长格式转换匹配(直观易维护)
通过将宽格式数据转为长格式,完成匹配后再转回宽格式,逻辑更直观,适合复杂匹配场景:
df_long_result <- df %>% # 收入列转长格式:保留ID、收入值 pivot_longer( starts_with("revenues_"), names_to = "rev_id", values_to = "rev_value" ) %>% # 销售列转长格式:保留年份、销售值 pivot_longer( starts_with("sales_"), names_to = "sales_year", values_to = "sales_value" ) %>% # 提取年份数字 mutate(year = str_extract(sales_year, "\\d+") %>% as.integer()) %>% # 匹配收入值与销售值 filter(rev_value == sales_value) %>% # 清理冗余列 select(-sales_year, -sales_value) %>% # 转回宽格式:生成year_X列 pivot_wider( names_from = rev_id, values_from = year, names_prefix = "year_" ) %>% # 与原数据合并 bind_cols(df, .) %>% # 调整列顺序(可选) select(starts_with("revenues_"), starts_with("sales_"), starts_with("year_")) # 查看结果 df_long_result
该方案输出与方案1完全一致。
内容的提问来源于stack exchange,提问作者Rico
相关产品推荐
相关产品推荐

