【问题标题】:Are there limits to tidy evaluation scope?整洁的评估范围是否有限制?
【发布时间】:2018-05-11 20:37:54
【问题描述】:

我正在尝试使用 dplyr 0.7.0 中定义的 tidy 评估。

但是,在mutate() 中的函数调用期间,我遇到了错误。似乎没有像我预期的那样评估变量。

library(dplyr)
library(tibble)
library(tidyr)

myCor <- function(col1, col2) {
  col1 <- enquo(col1)
  col2 <- enquo(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

myCor("mpg", "disp")
# Error in mutate_impl(.data, dots) : 
#   Evaluation error: 'x' must be numeric.

相反,我必须使用这种不整洁的 eval 语法来获得所需的输出。

myCor <- function(col1, col2) {
  col1_tidy <- enquo(col1)
  col2_tidy <- enquo(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1_tidy, !!col2_tidy) %>%
    mutate(correlation=cor(eval(parse(text=col1)), eval(parse(text=col2))))
}

myCor("mpg", "disp")
# vehicle  mpg  disp correlation
# 1            Mazda RX4 21.0 160.0  -0.8475514
# 2        Mazda RX4 Wag 21.0 160.0  -0.8475514
# 3           Datsun 710 22.8 108.0  -0.8475514
# 4       Hornet 4 Drive 21.4 258.0  -0.8475514
# ...

有没有办法在整个示例中使用整洁的评估?

【问题讨论】:

  • myCor(mpg, disp) 调用你的第一个函数(不需要引号,这就是 nse 的乐趣 :))
  • 比较 myCor(mpg, disp)myCor("mpg", "disp") 引号有很大的不同。有了它们,你就有了性格价值,没有它们,你就有了符号。 NSE 通常假定您有符号。
  • 如果您确实需要将字符串转换为字符值转换为符号,最好使用as.name()rlang::sym() 而不是parse(text=))
  • select 可以处理字符串作为输入,mutate 不能——它期望“裸”列名称发生了变异。 select 在那里更灵活
  • rlang::sym() 可以处理字符串和裸列名称,这比quoenquo 是一个很好的奖励。看起来select 使用的是sym 或其复数形式symsmutate 使用的是enquo

标签: r dplyr rlang tidyeval


【解决方案1】:

如果您想将字符串作为参数传递,请使用 ensym 而不是 enquo...

library(dplyr)
library(rlang)
library(tibble)

myCor <- function(col1, col2) {
  col1 <- ensym(col1)
  col2 <- ensym(col2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

# both of these will work now
myCor("mpg", "disp")
myCor(mpg, disp)

【讨论】:

  • CRAN 版本的 dplyr 导入 rlang,所以我认为这在技术上是不必要的
【解决方案2】:

来自小插曲Programming with dplyr

大多数 dplyr 参数不是引用透明的。这意味着您不能用您在别处定义的看似等效的对象替换值。

因此,您需要将不带引号的列名传递给函数,然后使用 enquo 正确捕获环境并且 !! 返回未带引号的列名,如 mutate 所期望的那样。

要将普通的mutate-call 转换为具有非标准评估的函数,这样开始可能更直观。
首先像没有函数一样写下调用:

mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, mpg, disp) %>%
    mutate(correlation = cor(mpg, disp))

这有效(并且引用 mpgdisp 会引发错误!)。
现在拉变量,你想在调用前改变并替换它们:

col1 <- quo(mpg)
col2 <- quo(disp)

mtcars %>%
  rownames_to_column("vehicle") %>%
  select(vehicle, !!col1, !!col2) %>%
  mutate(correlation=cor(!!col1, !!col2))

由于这是在函数之外,我们必须在这里使用quo,但在最后一步中,当我们将其包装在函数中时,我们使用enquo

myCor <- function(var1, var2) {
  col1 <- enquo(var1)
  col2 <- enquo(var2)

  mtcars %>%
    rownames_to_column("vehicle") %>%
    select(vehicle, !!col1, !!col2) %>%
    mutate(correlation=cor(!!col1, !!col2))
}

我为函数参数使用不同的名称,然后使用“引用”对象(enquo)使区别更清晰,但当然它也适用于col1col2

【讨论】:

    猜你喜欢
    • 2018-06-10
    • 2020-04-28
    • 2020-11-29
    • 2017-11-16
    • 1970-01-01
    • 2011-10-04
    • 2020-04-11
    • 1970-01-01
    • 2021-01-08
    相关资源
    最近更新 更多