如何用dplyr的mutate_at对成对列执行需多参数的函数?
当然可以实现这个需求!不过用mutate_at(或者现在更推荐的across())的时候,需要通过列名关联的方式,把每一组的_1列和对应的_2列配对起来——你之前的尝试之所以失败,是因为没法直接在funs里把x_1对应到x_2、y_1对应到y_2,得用字符串处理来动态找到对应列。
先拿你的示例数据来演示:
library(dplyr) df <- data.frame( x_1 = c(1, NA, 3), x_2 = c(1, 2, 4), y_1 = c(NA, 2, 1), y_2 = c(5, 6, 7) )
方法1:用dplyr最新的across()语法(推荐)
across()是dplyr 1.0.0之后替代mutate_at的函数,更灵活直观:
result <- df %>% mutate( # 针对所有以_1结尾的列处理 across(ends_with("_1"), # 动态找到对应的_2列:把当前列名的_1换成_2,用get()获取该列的值 ~ ifelse(is.na(.), get(sub("_1", "_2", cur_column())), .), # 设置新列名:去掉原列名的_1后缀 .names = "{sub('_1', '', .col)}") ) print(result) #> x_1 x_2 y_1 y_2 x y #> 1 1 1 NA 5 1 5 #> 2 NA 2 2 6 2 2 #> 3 3 4 1 7 3 1
这里cur_column()会返回当前正在处理的列名(比如x_1),通过sub("_1", "_2", ...)把它转换成x_2,再用get()提取这个列的数值,就能完美配对每一组列了。
方法2:用旧版的mutate_at(兼容dplyr旧版本)
如果你还在使用dplyr 1.0.0之前的版本,可以用mutate_at配合重命名来实现:
result_old <- df %>% # 对所有_1结尾的列处理,生成带_new后缀的临时列 mutate_at(vars(ends_with("_1")), funs(new = ifelse(is.na(.), get(sub("_1", "_2", cur_column())), .))) %>% # 把临时列的_1_new后缀去掉,得到最终的x、y列 rename_at(vars(ends_with("_new")), ~ sub("_1_new", "", .))
方法3:转长格式处理(适合列数很多的场景)
如果你的数据集有成十上百组这样的列,转成长格式处理会更清晰,避免写复杂的列名匹配:
library(tidyr) result_long <- df %>% # 把宽格式转成长格式,拆分列名为分组(x/y)和编号(1/2) pivot_longer(everything(), names_to = c("group", "num"), names_sep = "_") %>% # 转回宽格式,让每组的1和2列并排 pivot_wider(names_from = num, values_from = value) %>% # 合并1和2列:如果1列是NA就用2列的值 mutate(merged = ifelse(is.na(`1`), `2`, `1`)) %>% # 只保留分组和合并后的值,再转回宽格式 select(group, merged) %>% pivot_wider(names_from = group, values_from = merged) %>% # 和原数据合并 bind_cols(df, .)
简单总结一下:核心思路是通过列名字符串的替换,动态找到每一组对应的备用列,而不是直接在函数里硬编码列名——这样不管你有多少组列,都能一次性处理完成。
内容的提问来源于stack exchange,提问作者Bob
相关产品推荐
相关产品推荐

