基于ifelse条件和函数批量生成R数据框列(参数关联列名)
问题描述
我有如下测试数据集:
test_df <- data.frame(A =c(1, 2, 3, 3, 4), AKH_UL =c(111, 222, 333, 444, 555), AKH_LL = c(222, 333, 444, 555, 666), AKH_UU = c(213, 242, 253, 546, 243), AKH_LU = c(453, 855, 784, 352, 585), FFL_UL =c(111, 222, 333, 444, 555), FFL_LL = c(222, 333, 444, 555, 666), FFL_UU = c(213, 242, 253, 546, 243), FFL_LU = c(453, 855, 784, 352, 585))
我需要生成AKH和FFL这类列(实际要生成10列),列值依赖于A列的条件,不同条件对应不同自定义函数:
简化后的函数如下:
# Case 1: myfunction1 <- function(cost_LL, cost_UL, cost_LU, cost_UU){ cost_LL * cost_UL + cost_UU * cost_LU } # Case 2: myfunction2 <-function(cost_LL, cost_LU){ cost_LL * cost_LU } # Case 3: myfunction3 <-function(cost_UL, cost_UU){ cost_UL * cost_UU }
目前我为每列单独编写嵌套ifelse代码生成列,例如AKH列的代码:
test_df$AKH <- ifelse(test_df$A == 1, myfunction1(test_df$AKH_LL, test_df$AKH_UL, test_df$AKH_LU, test_df$AKH_UU), ifelse(test_df$A == 2, myfunction2(test_df$AKH_LL, test_df$AKH_LU), ifelse(test_df$A == 3, myfunction3(test_df$AKH_UL, test_df$AKH_UU), 99999)))
FFL列则替换AKH相关列名重复操作,写法冗余不优雅。我曾参考相关问题,但无法解决公式变量名与数据框列名关联的问题,求优化方案。
优化方案
首先我得先修正你原来的函数——直接在函数里修改全局的test_df不是个好习惯,改成返回计算结果的纯函数,这样代码更安全也更灵活。接下来我们用批量处理的方式解决重复代码的问题:
方案1:基础循环(新手友好,易理解)
把需要处理的列前缀(比如AKH、FFL)做成一个列表,然后循环处理每个前缀,动态拼接对应的列名:
# 先修正函数,返回计算值而非直接修改数据框 myfunction1 <- function(cost_LL, cost_UL, cost_LU, cost_UU){ cost_LL * cost_UL + cost_UU * cost_LU } myfunction2 <-function(cost_LL, cost_LU){ cost_LL * cost_LU } myfunction3 <-function(cost_UL, cost_UU){ cost_UL * cost_UU } # 定义所有需要生成的列前缀(10列就写10个前缀即可) prefix_list <- c("AKH", "FFL") # 循环遍历每个前缀 for(prefix in prefix_list){ # 动态生成对应后缀的列名 col_LL <- paste0(prefix, "_LL") col_UL <- paste0(prefix, "_UL") col_LU <- paste0(prefix, "_LU") col_UU <- paste0(prefix, "_UU") # 用case_when替代嵌套ifelse,逻辑更清晰 test_df[[prefix]] <- dplyr::case_when( test_df$A == 1 ~ myfunction1(test_df[[col_LL]], test_df[[col_UL]], test_df[[col_LU]], test_df[[col_UU]]), test_df$A == 2 ~ myfunction2(test_df[[col_LL]], test_df[[col_LU]]), test_df$A == 3 ~ myfunction3(test_df[[col_UL]], test_df[[col_UU]]), TRUE ~ 99999 # 其他情况的默认值 ) }
方案2:Tidyverse风格(更简洁,适合熟悉dplyr/purrr的用户)
用purrr::map_dfc批量生成新列,再合并到原数据框,代码更紧凑:
library(dplyr) library(purrr) # 同样先修正函数(和方案1一致) myfunction1 <- function(cost_LL, cost_UL, cost_LU, cost_UU){ cost_LL * cost_UL + cost_UU * cost_LU } myfunction2 <-function(cost_LL, cost_LU){ cost_LL * cost_LU } myfunction3 <-function(cost_UL, cost_UU){ cost_UL * cost_UU } prefix_list <- c("AKH", "FFL") # 批量生成新列 new_columns <- map_dfc(prefix_list, function(prefix){ col_LL <- paste0(prefix, "_LL") col_UL <- paste0(prefix, "_UL") col_LU <- paste0(prefix, "_LU") col_UU <- paste0(prefix, "_UU") case_when( test_df$A == 1 ~ myfunction1(test_df[[col_LL]], test_df[[col_UL]], test_df[[col_LU]], test_df[[col_UU]]), test_df$A == 2 ~ myfunction2(test_df[[col_LL]], test_df[[col_LU]]), test_df$A == 3 ~ myfunction3(test_df[[col_UL]], test_df[[col_UU]]), TRUE ~ 99999 ) %>% set_names(prefix) # 给新列命名 }) # 合并新列到原数据框 test_df <- bind_cols(test_df, new_columns)
关键优化点
- 纯函数改造:原来的函数直接修改全局数据框,现在改成返回计算结果,避免了副作用,代码更可靠。
- 动态列名关联:通过
paste0拼接前缀和后缀,再用[[访问数据框列,完美解决了变量名和列名的关联问题。 - 批量处理:不管你要生成10列还是更多,只需要在
prefix_list里添加前缀即可,不用重复写逻辑代码。 - case_when替代嵌套ifelse:多层嵌套的
ifelse可读性极差,case_when让条件逻辑一目了然,后续维护也更方便。
内容的提问来源于stack exchange,提问作者Jana Keller
相关产品推荐
相关产品推荐

