如何在dplyr的join函数中给by参数传入多个不同名连接键
解决dplyr连接函数包装时动态指定异名连接键的问题
问题重现
你想封装full_join(及left_join/right_join/inner_join等同类连接函数),支持调用时指定两个数据集的不同连接键,比如官方示例的写法:
full_join(band_members, band_instruments2, by = c("name" = "artist"))
但直接在函数内用c(quo_name(by1) = quo_name(by2))会触发语法错误:Error: unexpected '=' in: "full_join(data1, band_instruments2, by = c(quo_name(by1) ="
错误原因
R的语法规则不允许在c()的命名参数位置使用动态计算的表达式(比如quo_name(by1)这类返回字符串的函数调用),必须提前构建好完整的命名向量,再传给by参数。
可行解决方案
方案1:用set_names构建连接向量
先将传入的符号参数转换为字符串,再用set_names创建符合要求的命名连接向量:
library(dplyr) join_wrapper <- function(data1 = band_members, by1, by2) { # 将传入的符号参数转换为字符串 by1_str <- as_name(enquo(by1)) by2_str <- as_name(enquo(by2)) data2 <- band_instruments2 # 构建命名规则:data1的by1_str对应data2的by2_str by_rule <- set_names(by2_str, by1_str) full_join(data1, data2, by = by_rule) } # 调用测试 join_wrapper(band_members, name, artist)
方案2:直接赋值构建命名向量
如果不习惯set_names,也可以通过向量下标赋值的方式构建连接规则:
join_wrapper <- function(data1 = band_members, by1, by2) { by1_str <- as_name(enquo(by1)) by2_str <- as_name(enquo(by2)) data2 <- band_instruments2 by_rule <- c() by_rule[by1_str] <- by2_str full_join(data1, data2, by = by_rule) }
方案3:重命名data2列后再连接
如果更倾向于通过列名对齐实现连接,可以用dplyr::rename的tidyeval语法修改data2的列名,再用同名键连接:
join_wrapper <- function(data1 = band_members, by1, by2) { data2 <- band_instruments2 %>% # 将data2的by2列重命名为data1的by1列名 rename({{by1}} := {{by2}}) full_join(data1, data2, by = as_name(enquo(by1))) }
补充:关于assign的问题
你之前尝试用assign修改列名失败,是因为assign用于修改环境中的变量,而非数据框的列名。修改数据框列名应该用rename(如上方案3)或直接操作colnames,比如:
colnames(data2)[colnames(data2) == by2_str] <- by1_str
内容的提问来源于stack exchange,提问作者ESELIA
相关产品推荐
相关产品推荐

