【问题标题】:How to test if a function argument is a "quosurable" name?如何测试函数参数是否是“quosurable”名称?
【发布时间】:2020-07-02 10:13:09
【问题描述】:

我正在创建一个函数,它应该能够为其第一个参数处理多个类:公式、字符、tidy-selection、var 名称......然后目标是使用 tidyselection 和 tidyselect::vars_select,除了裸公式。

问题是,当我测试这个参数的类时,如果值是一个需要整齐选择的名称,它会抛出一个错误,因为它会被认为是一个未找到的对象。

我找到了tryCatch 的解决方法,如果它的评估失败(因此如果它不存在于这个范围内),它会引用第一个参数。

library(rlang)
foo=function(.vars){
    .vars2=tryCatch(.vars, error=function(e) enquo(.vars))
    print(class(.vars2))
    print(class(.vars))
}

foo(Species) 
# [1] "quosure" "formula"
# Error in print(class(.vars)) : object 'Species' not found
# In addition: Warning message:
# In print(class(.vars)) : restarting interrupted promise evaluation

foo(~Species)
# [1] "formula"
# [1] "formula"

foo(1) 
# [1] "numeric"
# [1] "numeric"

foo("Species")
# [1] "character"
# [1] "character"

这对我来说似乎并不干净,因为我没有过滤我的具体情况就发现了所有错误。

是否有一个内置函数来测试这个,或者比这个解决方法更干净的解决方案?

【问题讨论】:

  • 添加了foo的完整输出。您最初的删节版本有点误导。如果我现在输入的内容与您得到的输出不匹配,请再次编辑。
  • @dww 我的错,你的编辑完全正确。

标签: r rlang quosure


【解决方案1】:

我认为没有一种功能可以让您避免沿不同输入类型的结构化控制流。

library(rlang)
library(tidyselect)
library(dplyr)

foo <- function(df, .vars){
  en_vars <- enquo(.vars)
  var_expr <- quo_get_expr(en_vars)
  
  if (is.name(var_expr)){
    vars_select(names(df), !! en_vars)
  } else if (is_formula(var_expr)) {
    vars_select(names(df), all.vars(.vars))
  } else {
    vars_select(names(df), .vars)
  }
}

iris_tbl <- as_tibble(iris)

foo(iris_tbl, Species) 
#>   Species 
#> "Species"

foo(iris_tbl, ~Species)
#>   Species 
#> "Species"

foo(iris_tbl, 1) 
#> Note: Using an external vector in selections is ambiguous.
#> ℹ Use `all_of(.vars)` instead of `.vars` to silence this message.
#> ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
#> This message is displayed once per session.
#>   Sepal.Length 
#> "Sepal.Length"

foo(iris_tbl, "Species")
#>   Species 
#> "Species"

reprex package (v0.3.0) 于 2020-06-21 创建

【讨论】:

    【解决方案2】:

    我认为以下是您正在尝试做的事情(此处仅使用 base R)。

    foo=function(.vars) {
      .vars2 = substitute(.vars)
      ifelse(is.symbol(.vars2), class(.vars2), class(.vars))
      }
    
    foo(Species) 
    #[1] "name"
    foo(~Species)
    #[1] "formula"
    foo(1)
    #[1] "numeric"
    foo("Species")
    #[1] "character"
    

    【讨论】:

    • 目标是将 tidyselection 与 tidyselect::vars_select 一起使用,除了裸公式。我在问题中澄清了这一点。你的回答很有趣(我从不使用替代品,所以我从没想过),但最后你似乎也需要tryCatch,所以它对我来说似乎不太干净。但也许我没听懂。
    • 抱歉,我以为您想要的是消除错误和警告消息。我的第一次尝试做到了。我将其更改为也消除了tryCatch,如果这就是您所说的“更清洁”的意思。如果 tidyverse 仍然不足以满足您的目的,那么我删除
    猜你喜欢
    • 2020-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 2022-07-06
    相关资源
    最近更新 更多