我有一个自定义函数,我正在使用rlang
从数据框中读取输入的变量。无论输入的参数是引用还是不引用,此函数都可以正常 >。但是,奇怪的是,当此函数与purrr::pmap
一起使用时,仅在引用参数时才有效。
所以我有两个问题:
为什么函数行为这样?
如何使用rlang
创建函数,即使在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 package(v0.2.0)创建于2018-05-21。
答案 0 :(得分:3)
问题是:R看到Sepal.Length, wt, len
符号,因此它试图查看当前环境并对其进行评估。当然,由于它们是数据框的列,因此导致错误。当你引用它们时,R没有尝试评估并返回值,因为它将它们视为字符串。
如果您将list
替换为base::alist
或dplyr::vars
或rlang::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 package(v0.2.0)创建于2018-05-21。
答案 1 :(得分:1)
问题不在于purrr
,真的。可以通过以下方式观察到相同的行为:
list(Sepal.Length) # Error: object 'Sepal.Length' not found
根据我的理解,当你将参数传递给你创建的函数时,!!
,enquo
等所有魔法都可用。 >。这就是为什么它可以直接将未加引号的字段名称传递给tryfn()
。
但是使用pmap()
,您需要在Sepal.Width
定义中添加字段名称(wt
,list
等),list
不是那样的 - 所以pmap
从来没有机会将事情传递给tryfn
,因为你的list
barf定义了。
将字段名称作为字符串传递正常,因为list
可以容纳该数据类型,然后pmap
有机会将它们映射到tryfn()
。
哈德利对quasiquotation with dplyr
的评论可能对您有用。
回答你的第二个问题:
如何使用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