将整齐的 select 参数传递给内部函数,同时保持 "missingness"

Passing tidy select arguments to inner functions while maintaining "missingness"

谁能帮我找出解决这个问题的规范“tidyeval”方法:

这是我的方法,但它不起作用。 问题:missing(var) returns TRUEouter()FALSEinner().

inner <- function(data, var) {
  print(missing(var))
  if (missing(var)) data else dplyr::select(data, {{ var }})
}

outer <- function(data, var) {
  print(missing(var))
  inner(data, !!rlang::enquo(var))     
}

outer(mtcars)

#> [1] TRUE
#> [1] FALSE
#> data frame with 0 columns and 6 rows

inner(mtcars)

#> [1] TRUE
#>                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
...

我天真的解决方案是在外部检查 missing(var) 并相应地更改对 inner() 的调用(见下文),但我想知道是否有一种方法可以传递 var 参数没有任何这样的检查?

# works as desired
outer <- function(data, var) {
  print(missing(var))
  if (missing(var)) inner(data) else inner(data, !!rlang::enquo(var))
}

啊,对不起!我不知道为什么我认为 !!enquo() 是必要的。事实证明,传递可选参数而不改变其“缺失”就这么简单:

inner <- function(data, var) {
  print(missing(var))
  if (missing(var)) {
    print(colnames(data))
  } else {
    print(colnames(dplyr::select(data, {{ var }})))
  }
}

outer <- function(data, var) {
  print(missing(var))
  inner(data, var)
}

outer(mtcars, dplyr::starts_with("m"))

#> [1] FALSE
#> [1] FALSE
#> [1] "mpg"

outer(mtcars)

#> [1] TRUE
#> [1] TRUE
#> [1] "mpg"  "cyl"  "disp"  "hp"  "drat" "wt" "qsec" "vs" "am"  "gear" "carb"

编辑:显示如何查询缺失并在参数不缺失时应用整洁选择。

一种方法是给 var 足够的默认参数并无条件地将其传递给 select():

inner <- function(data, var = everything()) {
  dplyr::select(data, {{ var }})
}
outer <- function(data, var = everything()) {
  inner(data, {{ var }})
}

names(outer(mtcars))
#>  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear" "carb"

names(outer(mtcars, starts_with("d")))
#> [1] "disp" "drat"

对于更复杂的情况(希望不需要),您需要解开 !!enquo() 步骤,它们在单个 {{ 操作中合并。然后你可以检查解散的quosure:

inner <- function(data, var) {
  var <- enquo(var)
  if (quo_is_missing(var)) {
    data
  } else {
    dplyr::select(data, !!var)
  }
}
outer <- function(data, var) {
  inner(data, {{ var }})
}