【问题标题】:tidy evaluation in tidyrtidyr 中的整洁评估
【发布时间】:2020-04-28 20:54:46
【问题描述】:

这是关于vignette("in-packages")中代码的问题

数据集如下。

(mini_iris <- iris %>% 
    as_tibble() %>% 
    .[c(1, 2, 51, 52, 101, 102), ])
#> # A tibble: 6 x 5
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
#>          <dbl>       <dbl>        <dbl>       <dbl> <fct>     
#> 1          5.1         3.5          1.4         0.2 setosa    
#> 2          4.9         3            1.4         0.2 setosa    
#> 3          7           3.2          4.7         1.4 versicolor
#> 4          6.4         3.2          4.5         1.5 versicolor
#> 5          6.3         3.3          6           2.5 virginica 
#> 6          5.8         2.7          5.1         1.9 virginica

如果列名在字符向量中(可能来自函数调用),您可以将其提供给 one_of()

nest_egg <- function(data, cols) {
  nest(data, egg = one_of(cols))
}

nest_egg(mini_iris, c("Petal.Length", "Petal.Width", "Sepal.Length", "Sepal.Width"))
#> # A tibble: 3 x 2
#>   Species               egg
#>   <fct>      <list<df[,4]>>
#> 1 setosa            [2 × 4]
#> 2 versicolor        [2 × 4]
#> 3 virginica         [2 × 4]

然后,小插图描述了这一点

这里one_of()的使用很重要;如果不使用它,并且 data 包含名为 cols 的列,nest() 将嵌套它而不是 cols 中命名的列。

我认为使用tidy评估可以解决。

library(rlang)
nest_egg2 <- function(data, cols) {
  cols <- enexprs(cols)
  nest(data, egg = !!!cols)
}
nest_egg2(mini_iris, c("Petal.Length", "Petal.Width", "Sepal.Length", "Sepal.Width"))

但它显示错误

Error: `!!!` can't be supplied with a name. Only the operand's names are retained.

在下一节中,Vignette 描述了这一点

要提供一个与您包装的 tidyr 函数类似的接口,您应该使用 {{ arg }} 传递参数。 {{ }} 是一个特殊的 tidy eval 运算符,它捕获用户提供的表达式并将其转发给另一个启用 tidy eval 的函数。

nest_egg <- function(df, cols) {
  nest(df, egg = {{ cols }})
}

nest_egg(mini_iris, -Species)

但我想知道我的nest_egg2 有什么问题

【问题讨论】:

  • 是的,我正在尝试解决nest_egg2
  • 如果你这样做nest_egg2 &lt;- function(data, cols) { cols &lt;- enquo(cols); nest(data, egg = !! cols) }就可以了
  • 明确地说,您的目标是拥有一个可以提供裸列名称的函数,就像使用 dplyr 等函数一样?还是要将列名作为字符向量给出?

标签: r tidyverse tidyr tidyeval


【解决方案1】:

所以这里有两件事;

  1. nest 函数接受一个向量,但 enexprs 返回一个列表
  2. 您不需要 (bang bang/eval) 嵌套。大多数情况下,需要评估的是函数调用不带引号“”的列的名称,但是在您的情况下,因为 cols 已经是字符串的向量,它无需评估就可以正常工作。

所以这应该可行;

library(dplyr)
library(tidyr)

mini_iris <- 
  iris %>% 
  as_tibble() %>% 
  .[c(1, 2, 51, 52, 101, 102), ]

nest_egg2 <- function(data, cols) {
  nest(data, egg = cols)
}

nest_egg2(mini_iris, c("Petal.Length", "Petal.Width", "Sepal.Length", "Sepal.Width"))

# # A tibble: 3 x 2
# Species               egg
# <fct>      <list<df[,4]>>
# setosa            [2 x 4]
# versicolor        [2 x 4]
# virginica         [2 x 4]

【讨论】:

  • 从下一个版本开始,提供这样的环境变量将触发一条消息。将来,这将被弃用。正确的做法是使用one_of()(当前版本)或all_of()(下一个版本)。见tidyselect.r-lib.org/news/…
【解决方案2】:

在选择上下文中(而不是像mutate() 这样的动作上下文),如果需要命名,可以在c() 中使用!!!

nest_egg <- function(df, cols) {
  nest(df, egg = c(!!!cols))
}

由于 tidyselect 也支持名称向量,你也可以简单地取消引用,这两种方法都有效:

nest_egg <- function(df, cols) {
  nest(df, egg = !!cols)
}

取消引用可确保选择环境变量 cols 而不是数据变量 cols,因为 df 有一个名为 `cols 的列。

但在选择上下文中真正正确的方法是简单地使用one_of()(或在下一版本中使用all_of())。

【讨论】:

    猜你喜欢
    • 2018-06-10
    • 2020-11-29
    • 2017-11-16
    • 1970-01-01
    • 2021-01-08
    • 2018-07-18
    • 2021-12-01
    • 2019-09-17
    • 2019-02-28
    相关资源
    最近更新 更多