Dplyr通过=(a = b)加入,其中a和b是包含字符串的变量?

时间:2015-02-08 20:34:24

标签: r dplyr

我正在尝试使用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")

这会返回以下错误:

Error: cannot join on columns 'xname' x 'Sepal.Width': index out of bounds

我怀疑有一些奇特的表达,贬低,引用或不引用我可以做的工作,但我对这些细节有点模糊。

4 个答案:

答案 0 :(得分:28)

您可以使用

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()动态设置向量的名称。

答案 1 :(得分:2)

我知道我迟到了,但是怎么样:

myfn <- function(byvar) {
  data(iris)
  inner_join(iris, iris, by=byvar)
}

这样你可以做你想做的事情:

myfn(c("Sepal.Length"="Sepal.Width"))

答案 2 :(得分:0)

我面临与@Peter几乎相同的挑战,但是需要一次传递多个不同的by =连接参数集。我选择使用tidyverse软件包map()中的purrr函数。

这是我使用的tidyverse的子集。

library(magrittr)
library(dplyr)
library(rlang)
library(purrr)

首先,我将myfn修改为在Peter发表的案例中使用map()。 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_i中的每个by_grps,我们可以扩展xy以添加要连接的命名值。

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)

答案 3 :(得分:0)

我喜欢MrFlick的回答和fber的附录,但我更喜欢structure。对我来说,setNames感觉就像是管道的末端,而不是即时构造函数。另一方面,setNamesstructure都允许在函数调用中使用变量。

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"))

不过,您可以通过在函数调用中使用setNamesstructure来解决此问题。