Select 命名为 [list] 的元素使用 tidy 求值

Select named [list] element using tidy evaluation

我正在努力解决 rlang 包中解释的非标准评估。考虑到这个目标,我的问题是:

How do I write a dplyr::select.list() function that is consistent with tidy evaluation principles?

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

select_wrapper <- function(x, ...) {
  vars <- rlang::quos(...)
  dplyr::select(x, !!!vars)
}

这适用于数据帧,例如,

> 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

但不在列表中:

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: 

老实说,我不确定上面的输出中发生了什么......它 returns 一个交互式提示询问我 select 我想要哪个元素。这不太理想,imo。

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

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)

是否有更清洁、更整洁的评估一致方法来执行此操作?

我认为 dplyr 是用于 dfs

select 之所以用列表做一些超级奇怪的事情是因为它并不是真正为此而生的。我什至不确定为什么会发生这种情况(我 运行 曾经进入过那个交互式列表的东西并且非常困惑)。

[ 对于列表

select

但是[接受字符串。所以你的问题实际上是将裸参数转换为字符串以用于 [:

library(tidyverse)
l <- letters
names(l) <- letters
l
select.list <- function(x, ...) {
  vars <- rlang::quos(...) %>% map(quo_text) %>% unlist()
  x[vars]
}

select.list(l, a, b)
  a   b 
"a" "b" 

这里,quos return是一个quosores列表;为此,我映射 quo_text 以将每个元素从一个 quosure 转换为一个字符串,然后取消列出这些元素。然后就可以直接在列表上调用[x[vars]到return一个命名元素的列表。

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

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"))

请注意,当您不拥有泛型(例如 select() 属于 dplyr)或 class(例如 list 来自R核心)。