首页 > 解决方案 > r rlang:在 tidyselect 助手上使用 is_quosure

问题描述

假设您在 R 函数中有一个参数,它可以是:

  1. 一个原始的 tidyselect 助手,例如contains("a")starts_with("a")等等,
  2. 带有助手的 quosures 列表,带有vars(contains("a")).

如果您在情况(1)或(2)中,您如何检查功能?

问题是它is_quosures(vars(contains("a")))有效,但is_quosures(contains("a"))不起作用,因为它首先尝试评估函数contains("a"),当单独评估时返回错误!?

library(rlang)
library(dplyr)
is_quosures(vars(contains("a")))
#> [1] TRUE
is_quosures(contains("a"))
#> Error: No tidyselect variables were registered

fo <- function(var) {
  is_quosures(var)  
}

fo(vars(contains("a")))
#> [1] TRUE
fo(contains("a"))
#> Error: No tidyselect variables were registered

reprex 包(v0.3.0)于 2019 年 12 月 3 日创建

用例

您想使用诸如 , 之类的函数summarise_at(data, var),并希望使其对指定var为直接tidyselect助手或包装在vars()调用中的用户具有鲁棒性。我想出解决这个问题的唯一方法是逐案进行 if/then 检查它是否是 quosure (如果不是,则包装到 vars 中),但这在上述情况下将完全失败。

library(rlang)
library(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

fo <- function(var) {
  is_var_closure <- rlang::is_quosures(var)
  if(is_var_closure) {
    dplyr::summarise_at(iris, var, mean)  
  } else {
    dplyr::summarise_at(iris, quos(var), mean)  
  }
}


fo(vars(starts_with("Sepal")))
#>   Sepal.Length Sepal.Width
#> 1     5.843333    3.057333
fo(starts_with("Sepal"))
#> Error: No tidyselect variables were registered

reprex 包(v0.3.0)于 2019 年 12 月 3 日创建

标签: rtidyverserlangquosure

解决方案


我之前这样做的方法是捕获表达式并检查is_call

f <- function(var) {
  if (rlang::is_call(rlang::enexpr(var), names(tidyselect::vars_select_helpers))) {
    rlang::enquos(var)
  }
  else {
    stopifnot(rlang::is_quosures(var)) # or something more specific with a useful message
    var
  }
}

# both work
f(vars(starts_with("Sepal")))
f(starts_with("Sepal"))

只需确保使用enexprfor is_call,请参阅此 GitHub 问题


推荐阅读