【问题标题】:How to initialize a variable with a tidyselect helper?如何使用 tidyselect 助手初始化变量?
【发布时间】:2020-03-22 20:26:03
【问题描述】:

我在某个函数中使用了 tidyselection,我必须将第一个参数与省略号连接起来,因为它可能是需要特定处理的特定类。

正常行为是这样的:

foo = function(x, ...){
    xloc = eval_select(expr(c(x, ...)), data = iris)
    return(xloc)
}
foo(everything())

xNULL 时,我希望将everything() 作为默认值(由于某种原因,我不能将其直接放在标题中)。

很遗憾,这种语法是不允许的:

bar = function(x, ...){
    if(is_null(x))
        x=everything() #throws an error
    xloc = eval_select(expr(c(x, ...)), data = iris)
    return(xloc)
}
bar(NULL)
# Error: `everything()` must be used within a *selecting* function.
# i See <https://tidyselect.r-lib.org/reference/faq-selection-context.html>.

我试图用我知道的所有“神秘”函数包装everything()parsedeparsecallsubstitutequosymenquoensym , ... 没有任何效果(你可以在这里看到我没有很好地掌握这些)。

我可以用什么表达式替换我的第二个代码块中的 x=everything() 行以使此函数起作用?

版本:

  • tidyselect 版本 1.0.0
  • rlang 版本 0.4.5
  • dplyr 版本 0.8.5

【问题讨论】:

    标签: r rlang tidyselect


    【解决方案1】:

    首先你需要通过{{传递x,否则tidyselect无法检查参数,并且某些功能将无法正常工作。然后你可以给它一个默认的everything()

    foo <- function(x = everything(), ...) {
      eval_select(expr(c({{ x }}, ...)), data = iris)
    }
    
    foo(everything())
    #> Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
    #>            1            2            3            4            5
    
    foo()
    #> Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
    #>            1            2            3            4            5
    

    如果由于某种原因您不能使用默认参数,请手动解除everything(),然后使用!! 强制它:

    foo <- function(x = NULL, ...) {
      x <- enquo(x)
    
      if (quo_is_null(x)) {
        x <- expr(everything())
      }
    
      eval_select(expr(c(!!x, ...)), data = iris)
    }
    

    【讨论】:

    • 正如我在问题中所说,“由于某种原因,我不能将它直接放在标题中”。我真的而且只想在函数内部用everything() 初始化一个x 变量。此外,你的回答让我有点吃惊,因为这在我的第一个代码块中运行良好,没有卷曲。
    • 对于依赖动态上下文的选择助手来说工作正常。但它会因其他类型的 tidyselect 功能而崩溃。不推荐使用没有all_of()any_of() 的上下文变量,您没有看到警告吗?
    • 添加了没有默认的示例。
    • 就是这样,它有效!非常感谢你的帮助。我看到了警告,但我想我以后会处理它。您可能想在帮助文件中添加类似的内容,使用 curly-curly、all_of()any_of(),这对像我这样的人很有帮助。
    【解决方案2】:

    我们可以将everything 包裹在eval_select

    bar <- function(x, ...){
    
        xloc <- tidyselect::eval_select(expr(c(x, ...)), data = iris)
        if(length(xloc) == 0) {
         xloc <- tidyselect::eval_select(expr(everything()), data = iris)
          }
        xloc
    }
    
    
    
    bar(1:2)
    #Sepal.Length  Sepal.Width 
    #           1            2 
    bar(NULL)
    #Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
    #           1            2            3            4            5 
    

    或者我们可以在expr 中使用if/else 条件

    bar <- function(x, ...) {
    
        x1 <-  expr(c(if(is_null(x)) everything() else x, ...))
        tidyselect::eval_select(x1, data = iris)
    
    }
    
    bar(everything())
    #Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
    #           1            2            3            4            5 
    bar(NULL)
    #Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
    #           1            2            3            4            5 
    

    【讨论】:

    • 我知道我们可以,但在这种特定情况下它会缺乏我需要的灵活性。有没有办法把它放在变量中?
    • @DanChaltiel everything() 在 tidyverse 函数上下文之外不被评估
    • 不是var1x,也不是像我的第二个代码块中那样的字符串
    • @DanChaltiel 我使用修改后的选项进行了更新。请检查这是否适合您。在这里,是单个expr
    • 感谢您抽出宝贵时间,但是,虽然所有这些都有效,但这不是我要的,因为这不符合限制条件。我真的只想在函数内部用everything() 初始化一个x 变量。
    猜你喜欢
    • 2011-01-08
    • 2020-03-28
    • 1970-01-01
    • 2019-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 2012-10-11
    相关资源
    最近更新 更多