将 curly-curly (`{{ }}`) 运算符与 `if` 子句一起使用时出错

Error when using curly-curly (`{{ }}`) operator with `if` clause

我正在努力理解如何使用 {{ }} 运算符在自定义函数中传递裸变量名。当我将运算符与 if 子句结合使用时出现错误。

此功能有效:

f <- function(.data, .vars=NULL){
  require(dplyr)
  df = select(.data, {{ .vars }})
  print(head(df))
}

f(iris, c(Species, Sepal.Length))
#> Loading required package: dplyr
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
#>   Species Sepal.Length
#> 1  setosa          5.1
#> 2  setosa          4.9
#> 3  setosa          4.7
#> 4  setosa          4.6
#> 5  setosa          5.0
#> 6  setosa          5.4

reprex package (v2.0.1)

于 2021-12-20 创建

如果我尝试添加 if 子句,它会抛出错误:

f <- function(.data, .vars=NULL){
  require(dplyr)
  if(!is.null(.vars)) df = select(.data, {{ .vars }})
  else df = .data
  print(head(df))
}

f(iris, c(Species, Sepal.Length))
#> Loading required package: dplyr
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
#> Error in f(iris, c(Species, Sepal.Length)): object 'Species' not found

reprex package (v2.0.1)

于 2021-12-20 创建

我错过了什么?

我认为最简单的解释是,当 .vars 不是 NULL 时,R 会将值(在您的示例中:c(Species, Sepal.Length))解释为变量向量,并查看对于您环境中的这些变量。由于您没有任何名为 Species 的变量,它会引发错误。

你可以这样修复:

library(dplyr)


f <- function(.data, .vars = NULL){
  
  vars <- enquo(.vars)
  
  if(!rlang::quo_is_null(vars)) df = select(.data, !!vars)
  else df = .data
  
  print(head(df))
  
}

f(iris) 
f(iris, c(Species, Sepal.Length))

请注意 {{x}} 实际上是 !!enquo(x) 的 shorthand。

详细说明(更新)

  • 当您不使用 if 时,唯一使用 .vars 的地方是在 dplyr::select(.data, {{.vars}}) 内。在这种情况下,.vars 中的变量名称被解释为数据帧 .data.

    中的变量
  • 当您添加 if 语句时,.vars 被评估为您环境中的变量。由于它们不存在于您的环境中,因此您会收到错误消息。

这称为数据屏蔽。 Here 是一个 关于它的好文章。

@jpiversen 的回答和解释是正确的,但这里有一个更简单的函数修复方法。不要寻找 NULL 的默认值,只需检查 .vars 是否丢失:

library(dplyr)

f <- function(.data, .vars){
  if(!missing(.vars)) df = select(.data, {{ .vars }})
  else df = .data
  print(head(df))
}

f(iris, c(Species, Sepal.Length))

顺便说一下,我还从你的函数中删除了 require(dplyr)。在函数中使用它通常不是一个好主意,因为更改搜索列表会产生副作用。如果您不确定它是否可用,请使用 requireNamespace("dplyr") 并使用 dplyr:: 作为前缀函数。