Dplyr join on by=(a = b),其中a和b是包含字符串的变量?
Dplyr join on by=(a = b), where a and b are variables containing strings?
我正在尝试使用 dplyr 对两个表执行内部联接,但我认为我被非标准评估规则绊倒了。使用 by=("a" = "b") 参数时,当 "a" 和 "b" 是实际字符串时,一切都按预期工作。这是一个有效的玩具示例:
library(dplyr)
data(iris)
inner_join(iris, iris, by=c("Sepal.Length" = "Sepal.Width"))
但假设我将 inner_join 放入函数中:
library(dplyr)
data(iris)
myfn <- function(xname, yname) {
data(iris)
inner_join(iris, iris, by=c(xname = yname))
}
myfn("Sepal.Length", "Sepal.Width")
此returns以下错误:
Error: cannot join on columns 'xname' x 'Sepal.Width': index out of bounds
我怀疑我可以做一些花哨的表达、解析、引用或取消引用来完成这项工作,但我对这些细节有点模糊。
您可以使用
myfn <- function(xname, yname) {
data(iris)
inner_join(iris, iris, by=setNames(yname, xname))
}
的 ?inner_join
文档中的建议语法
by = c("a"="b") # same as by = c(a="b")
有点误导,因为这两个值都不是正确的字符值。您实际上创建了一个命名的字符向量。动态设置等号左边的值和右边的不同。您可以使用 setNames()
动态设置向量的名称。
我知道我迟到了,但是怎么样:
myfn <- function(byvar) {
data(iris)
inner_join(iris, iris, by=byvar)
}
这样你就可以做你想做的事情了:
myfn(c("Sepal.Length"="Sepal.Width"))
我遇到了与@Peter 几乎相同的挑战,但需要一次传递多组不同的 by =
连接参数。我选择使用 tidyverse 包中的 map()
函数 purrr
.
这是我使用的 tidyverse 的子集。
library(magrittr)
library(dplyr)
library(rlang)
library(purrr)
首先,我将 myfn
改编为使用 map()
用于 Peter 发布的案例。 42 的评论和 Felipe Gerard 的回答清楚地表明 by
参数可以采用命名向量。 map()
需要一个要迭代的列表。
myfn_2 <- function(xname, yname) {
by_names <- list(setNames(nm = xname, yname ))
data(iris)
# map() returns a single-element list. We index to retrieve dataframe.
map( .x = by_names,
.f = ~inner_join(x = iris,
y = iris,
by = .x)) %>%
`[[`(1)
}
myfn_2("Sepal.Length", "Sepal.Width")
我发现我在构建函数时不需要 quo_name()
/ !!
。
然后,我调整函数以获取 by
参数列表。对于 by_grps
中的每个 by_i
,我们可以扩展 x
和 y
以添加要加入的命名值。
by_grps <- list( by_1 = list(x = c("Sepal.Length"), y = c("Sepal.Width")),
by_2 = list(x = c("Sepal.Width"), y = c("Petal.Width"))
)
myfn_3 <- function(by_grps_list, nm_dataset) {
by_named_vectors_list <- lapply(by_grps_list,
function(by_grp) setNames(object = by_grp$y,
nm = by_grp$x))
map(.x = by_named_vectors_list,
.f = ~inner_join(nm_dataset, nm_dataset, by = .x))
}
myfn_3(by_grps, iris)
我喜欢 MrFlick 的回答和 fber 的附录,但我更喜欢 structure
。对我来说 setNames
感觉就像管道末端的东西,而不是即时构造函数。另一方面,setNames
和 structure
都允许在函数调用中使用变量。
myfn <- function(xnames, ynames) {
data(iris)
inner_join(iris, iris, by = structure(names = xnames, .Data = ynames))
}
x <- "Sepal.Length"
myfn(x, "Sepal.Width")
命名向量参数会 运行 出现问题:
myfn <- function(byvars) {
data(iris)
inner_join(iris, iris, by = byvars)
}
x <- "Sepal.Length"
myfn(c(x = "Sepal.Width"))
不过,您可以通过在函数调用中使用 setNames
或 structure
来解决这个问题。
我正在尝试使用 dplyr 对两个表执行内部联接,但我认为我被非标准评估规则绊倒了。使用 by=("a" = "b") 参数时,当 "a" 和 "b" 是实际字符串时,一切都按预期工作。这是一个有效的玩具示例:
library(dplyr)
data(iris)
inner_join(iris, iris, by=c("Sepal.Length" = "Sepal.Width"))
但假设我将 inner_join 放入函数中:
library(dplyr)
data(iris)
myfn <- function(xname, yname) {
data(iris)
inner_join(iris, iris, by=c(xname = yname))
}
myfn("Sepal.Length", "Sepal.Width")
此returns以下错误:
Error: cannot join on columns 'xname' x 'Sepal.Width': index out of bounds
我怀疑我可以做一些花哨的表达、解析、引用或取消引用来完成这项工作,但我对这些细节有点模糊。
您可以使用
myfn <- function(xname, yname) {
data(iris)
inner_join(iris, iris, by=setNames(yname, xname))
}
的 ?inner_join
文档中的建议语法
by = c("a"="b") # same as by = c(a="b")
有点误导,因为这两个值都不是正确的字符值。您实际上创建了一个命名的字符向量。动态设置等号左边的值和右边的不同。您可以使用 setNames()
动态设置向量的名称。
我知道我迟到了,但是怎么样:
myfn <- function(byvar) {
data(iris)
inner_join(iris, iris, by=byvar)
}
这样你就可以做你想做的事情了:
myfn(c("Sepal.Length"="Sepal.Width"))
我遇到了与@Peter 几乎相同的挑战,但需要一次传递多组不同的 by =
连接参数。我选择使用 tidyverse 包中的 map()
函数 purrr
.
这是我使用的 tidyverse 的子集。
library(magrittr)
library(dplyr)
library(rlang)
library(purrr)
首先,我将 myfn
改编为使用 map()
用于 Peter 发布的案例。 42 的评论和 Felipe Gerard 的回答清楚地表明 by
参数可以采用命名向量。 map()
需要一个要迭代的列表。
myfn_2 <- function(xname, yname) {
by_names <- list(setNames(nm = xname, yname ))
data(iris)
# map() returns a single-element list. We index to retrieve dataframe.
map( .x = by_names,
.f = ~inner_join(x = iris,
y = iris,
by = .x)) %>%
`[[`(1)
}
myfn_2("Sepal.Length", "Sepal.Width")
我发现我在构建函数时不需要 quo_name()
/ !!
。
然后,我调整函数以获取 by
参数列表。对于 by_grps
中的每个 by_i
,我们可以扩展 x
和 y
以添加要加入的命名值。
by_grps <- list( by_1 = list(x = c("Sepal.Length"), y = c("Sepal.Width")),
by_2 = list(x = c("Sepal.Width"), y = c("Petal.Width"))
)
myfn_3 <- function(by_grps_list, nm_dataset) {
by_named_vectors_list <- lapply(by_grps_list,
function(by_grp) setNames(object = by_grp$y,
nm = by_grp$x))
map(.x = by_named_vectors_list,
.f = ~inner_join(nm_dataset, nm_dataset, by = .x))
}
myfn_3(by_grps, iris)
我喜欢 MrFlick 的回答和 fber 的附录,但我更喜欢 structure
。对我来说 setNames
感觉就像管道末端的东西,而不是即时构造函数。另一方面,setNames
和 structure
都允许在函数调用中使用变量。
myfn <- function(xnames, ynames) {
data(iris)
inner_join(iris, iris, by = structure(names = xnames, .Data = ynames))
}
x <- "Sepal.Length"
myfn(x, "Sepal.Width")
命名向量参数会 运行 出现问题:
myfn <- function(byvars) {
data(iris)
inner_join(iris, iris, by = byvars)
}
x <- "Sepal.Length"
myfn(c(x = "Sepal.Width"))
不过,您可以通过在函数调用中使用 setNames
或 structure
来解决这个问题。