tidyr 中的整洁评估

tidy evaluation in tidyr

这是关于 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]

然后,小插图描述

The use of one_of() here is important; if you don’t use it, and data contains a column named cols, nest() will nest it instead of the columns named in cols.

我认为可以使用 tidy evaluation.

来解决
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 描述了

To provide an interface that works like the tidyr function that you’re wrapping, you should pass the argument along using {{ arg }}. {{ }} is a special tidy eval operator that captures the expression supplied by the user and forwards it to another tidy eval-enabled function.

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

nest_egg(mini_iris, -Species)

但我想知道我的nest_egg2

中有什么问题

所以这里有两件事;

  1. nest 函数接受一个向量,但是 enexprs returns 一个列表
  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]

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

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

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

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

对于 df 有一个名为 `cols.

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