6

我有一个自定义函数,我正在使用rlang. 无论输入的参数是带引号还是不带引号,此函数都可以正常工作。但是,奇怪的是,当这个函数与 一起使用时purrr::pmap,它只有在参数被引用时才有效。

所以我有两个问题:

  1. 为什么函数会这样?

  2. 如何使用rlang这样的函数创建一个函数,即使使用 in 也不必引用参数purrr::pmap

这是一个使用简单函数来突出这个问题的最小表示:

# loading the needed libraries
library(rlang)
library(dplyr)
library(purrr)


# defining the function
tryfn <- function(data, x, y) {
  data <-
    dplyr::select(
      .data = data,
      x = !!rlang::enquo(x),
      y = !!rlang::enquo(y)
    )

  # creating a dataframe of means
  result_df <- data.frame(mean.x = mean(data$x), mean.y = mean(data$y))

  # return the dataframe
  return(result_df)
}

# without quotes (works!)
tryfn(iris, Sepal.Length, Sepal.Width)
#>     mean.x   mean.y
#> 1 5.843333 3.057333

# with quotes (works!)
tryfn(iris, "Sepal.Length", "Sepal.Width")
#>     mean.x   mean.y
#> 1 5.843333 3.057333

# pmap without quotes (doesn't work)
purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x = list(Sepal.Length, wt, len),
  y = list(Sepal.Width, mpg, dose)
),
.f = tryfn)
#> Error in is.data.frame(.l): object 'Sepal.Length' not found

# pmap with quotes (works!)
purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x = list("Sepal.Length", "wt", "len"),
  y = list("Sepal.Width", "mpg", "dose")
),
.f = tryfn)
#> [[1]]
#>     mean.x   mean.y
#> 1 5.843333 3.057333
#> 
#> [[2]]
#>    mean.x   mean.y
#> 1 3.21725 20.09062
#> 
#> [[3]]
#>     mean.x   mean.y
#> 1 18.81333 1.166667

reprex 包(v0.2.0) 于 2018 年 5 月 21 日创建。

4

2 回答 2

4

问题是:R 看到了Sepal.Length, wt, len符号,所以它试图在当前环境中查看并评估它们。当然,它会导致错误,因为它们是数据框的列。当您引用它们时,R 并没有尝试评估和返回值,因为它将这些值视为字符串。

如果您替换listbase::alistordplyr::varsrlang::exprs,它应该可以工作

注意:由于我们已经引用了输入,我们不再需要在rlang::enquo内部使用tryfn

# loading the needed libraries
library(rlang)
library(tidyverse)

# defining the function
tryfn <- function(data, x, y) {
  data <-
    dplyr::select(
      .data = data,
      x = !! x,
      y = !! y
    )

  # creating a data frame of means
  result_df <- data.frame(mean.x = mean(data$x), mean.y = mean(data$y))

  # return the data frame
  return(result_df)
}

# alist handles its arguments as if they described function arguments. 
# So the values are not evaluated, and tagged arguments with no value are 
# allowed whereas list simply ignores them. 

purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x    = alist(Sepal.Length, wt, len),
  y    = alist(Sepal.Width, mpg, dose)
),
.f = tryfn)

#> [[1]]
#>     mean.x   mean.y
#> 1 5.843333 3.057333
#> 
#> [[2]]
#>    mean.x   mean.y
#> 1 3.21725 20.09062
#> 
#> [[3]]
#>     mean.x   mean.y
#> 1 18.81333 1.166667


purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x    = dplyr::vars(Sepal.Length, wt, len),
  y    = dplyr::vars(Sepal.Width, mpg, dose)
),
.f = tryfn)

#> [[1]]
#>     mean.x   mean.y
#> 1 5.843333 3.057333
#> 
#> [[2]]
#>    mean.x   mean.y
#> 1 3.21725 20.09062
#> 
#> [[3]]
#>     mean.x   mean.y
#> 1 18.81333 1.166667

purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x    = rlang::exprs(Sepal.Length, wt, len),
  y    = rlang::exprs(Sepal.Width, mpg, dose)
),
.f = tryfn)

#> [[1]]
#>     mean.x   mean.y
#> 1 5.843333 3.057333
#> 
#> [[2]]
#>    mean.x   mean.y
#> 1 3.21725 20.09062
#> 
#> [[3]]
#>     mean.x   mean.y
#> 1 18.81333 1.166667

reprex 包(v0.2.0) 于 2018 年 5 月 21 日创建。

于 2018-05-22T03:52:07.450 回答
1

问题不在于purrr,真的。可以通过以下方式观察到相同的行为:

list(Sepal.Length) # Error: object 'Sepal.Length' not found

据我了解,当您将参数传递给您创建!!的函数时,所有带有,等的魔法都可用。这就是为什么直接将未引用的字段名称传递给它的原因。 enquotryfn()

但是使用pmap(),您将字段名称(Sepal.Widthwt等)放在list定义中,并且list不喜欢这样 - 因此,自从您的定义错误以来,您pmap甚至没有机会将内容传递进去。tryfnlist

将字段名称作为字符串传递就可以了,因为list可以容纳该数据类型,然后pmap有机会将它们映射到tryfn().

Hadley 对quasiquotation withdplyr的评论可能对您有用。

回答你的第二个问题:

如何使用 rlang 创建一个函数,这样即使在 purrr::pmap 中使用,我也不必引用参数?

您可以用 包装您的字段名称quo()以避免将它们作为字符串直接引用,尽管我不确定这是一个很大的改进:

purrr::pmap(.l = list(
  data = list(iris, mtcars, ToothGrowth),
  x = list(quo(Sepal.Length), quo(wt), quo(len)),
  y = list(quo(Sepal.Width), quo(mpg), quo(dose))
),
.f = tryfn) %>% 
  bind_rows(., .id="dataset")

  dataset    mean.x    mean.y
1       1  5.843333  3.057333
2       2  3.217250 20.090625
3       3 18.813333  1.166667
于 2018-05-22T02:44:26.793 回答