如何在R的dplyr中用mutate+across合并多组变量的mutate调用
当然可以把这三次mutate调用合并成一次!针对你这种成对变量的替换场景,有两种非常实用的方法,都能达到和原代码完全一致的效果:
先回顾下你的初始数据和变量定义
# Create example dataframe df <- data.frame(gp = c(0, 1, 0, 1), col1A = c(1, 2, 3, 4), col1B = c(5, 6, 7, 8), col2A = c(11, 12, 13, 14), col2B = c(15, 16, 17, 18), col3A = c(11, 12, 13, 14), col3B = c(15, 16, 17, 18)) aVars = c('col1A', 'col2A', 'col3A') bVars = c('col1B', 'col2B', 'col3B')
方法1:利用变量名的固定规则(无需依赖aVars/bVars)
如果你的A列和B列命名规则很规整(比如都是colX A和colX B的形式),可以用cur_column()动态获取当前处理的列名,再通过字符串替换得到对应的B列:
library(dplyr) library(stringr) df %>% mutate(across(all_of(aVars), ~ ifelse(gp == 0, get(str_replace(cur_column(), "A$", "B")), .x)))
这里str_replace(cur_column(), "A$", "B")会自动把当前的col1A转换成col1B,get()函数用来提取这个列的数值,完美替代重复的mutate操作。
方法2:直接使用给定的aVars和bVars对应关系
如果你已经明确有aVars和bVars的一一对应向量,可以结合purrr::imap和命名向量来实现:
library(dplyr) library(purrr) df %>% mutate(imap(set_names(bVars, aVars), ~ ifelse(gp == 0, .data[[.y]], .data[[.x]])))
这里set_names(bVars, aVars)把B列向量的名字设为对应的A列名称,imap会同时遍历名称(A列)和值(B列),然后用.data[[.y]]和.data[[.x]]分别获取对应的列数据,完成替换逻辑。
验证两种方法的正确性
你可以用all.equal()来验证,两种方法得到的结果和你原来三次mutate的结果完全一致:
# 原代码的结果 original_result <- df %>% mutate(col1A = ifelse(gp == 0, col1B, col1A)) %>% mutate(col2A = ifelse(gp == 0, col2B, col2A)) %>% mutate(col3A = ifelse(gp == 0, col3B, col3A)) # 方法1的结果 method1_result <- df %>% mutate(across(all_of(aVars), ~ ifelse(gp == 0, get(str_replace(cur_column(), "A$", "B")), .x))) all.equal(original_result, method1_result) # 返回 TRUE
这样不仅把三次mutate合并成了一次,后续如果有更多类似的列对,只需要更新aVars和bVars向量即可,代码的可维护性大幅提升!
内容的提问来源于stack exchange,提问作者Canovice
相关产品推荐
相关产品推荐

