使用 tidy 评估选择命名的 [list] 元素

mke*_*ney 4 r non-standard-evaluation tidyeval rlang

我试图理解rlang包中解释的非标准评估。考虑到这个目标,我的问题是:

如何编写dplyr::select.list()符合整洁评估原则的函数?

这是我当前如何编写包装器的示例dplyr::select()

select_wrapper <- function(x, ...) {
  vars <- rlang::quos(...)
  dplyr::select(x, !!!vars)
}
Run Code Online (Sandbox Code Playgroud)

适用于数据框,例如,

> select_wrapper(mtcars, cyl, mpg)
> ##                     cyl  mpg
> ## Mazda RX4             6 21.0
> ## Mazda RX4 Wag         6 21.0
> ## Datsun 710            4 22.8
> ## Hornet 4 Drive        6 21.4
> ## Hornet Sportabout     8 18.7
> ## Valiant               6 18.1
Run Code Online (Sandbox Code Playgroud)

但不在列表中:

attr(mtcars, "test") <- "asdf"
mtcars_list <- attributes(mtcars)
select_wrapper(mtcars_list, row.names, test)
> ## 1: c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb")
> ## 2: c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710", "Hornet 4 Drive", "Hornet Sportabout", "Valiant", "Duster 360", "Merc 240D", "Merc 230", "Merc 280", "Merc 280C", "Merc 450SE", "Merc 450SL", "Merc 450SLC", "Cadillac Fleetwood", "Lincoln Continental", "Chrysler Imperial", "Fiat 128", "Honda Civic", "Toyota Corolla", "Toyota Corona", "Dodge Challenger", "AMC Javelin", "Camaro Z28", "Pontiac Firebird", "Fiat X1-9", "Porsche 914-2", "Lotus Europa", "Ford Pantera L", "Ferrari Dino", "Maserati Bora", "Volvo 142E")
> ## 3: data.frame
> ## 4: asdf
> ## Selection: 
Run Code Online (Sandbox Code Playgroud)

老实说,我不确定上面的输出中发生了什么......它返回一个交互式提示,要求我选择我想要的元素。在我看来,这并不是很理想。

无论如何,我想要完成的是一个select.list()函数,它返回我通过非标准评估选择的命名元素的列表。这是我的解决方案,但感觉太老套了:

listdf <- function(x) {
  as.data.frame(lapply(x, function(x) I(list(x))))
}
dflist <- function(x) {
  x <- lapply(x, unlist, recursive = FALSE)
  lapply(x, unclass)
}
select.list <- function(x, ...) {
  dots <- rlang::quos(...)
  if (length(dots) == 0L) return(list())
  x <- listdf(x)
  dflist(dplyr::select(x, !!!dots))
}

library(dplyr)
attr(mtcars, "test") <- "asdf"

select(attributes(mtcars), test, row.names)
Run Code Online (Sandbox Code Playgroud)

有没有更干净、更整洁、评估一致的方法来做到这一点?

Lio*_*nry 6

您可以使用 tidyselect 实现后端select()

select2 <- function(.x, ...) {
  vars <- rlang::names2(.x)
  vars <- tidyselect::vars_select(vars, ...)
  .x[vars]
}

x <- list(a = 1, b = 2)
select2(x, dplyr::starts_with("a"))
Run Code Online (Sandbox Code Playgroud)

select()请注意,当您不拥有泛型(例如dplyr 拥有)或类(例如list来自 R 核心)时,实现 S3 方法是不好的做法。

  • 你的问题恰逢其时!我正在考虑实现一个通用的“tidyselect::vec_select()”,它将与列表和任何向量一起使用。 (2认同)