如何在 dplyr::select() 中使用动态 tidy-select 表达式?
How to use dynamic tidy-select expressions in dplyr::select()?
我需要 select 在多个表达式上动态变量。考虑以下示例:
library(tidyverse)
set.seed(42)
df <- tibble(
grey_dog = runif(n = 69),
white_bear = runif(n = 69),
blue_oyster = runif(n = 69),
white_lobster = runif(n = 69),
green_dog = runif(n = 69)
)
df %>%
dplyr::select(
(contains("dog") & contains("green")) |
(contains("white") & contains("bear"))
)
而不是显式 selecting,我有包含信息的向量,我想将我的 selection 基于:
x <- c(green = "dog", white = "bear")
所以我希望连接一个可以用作整洁的字符串-select:
s <- paste0("(", paste0("contains(", names(x),") & contains(", x, ")"), ")", collapse = " | ")
dplyr::select(df, s)
这失败了:
Error: Can't subset columns that don't exist.
x Column `(contains(green) & contains(dog)) | (contains(white) & contains(bear))` doesn't exist.
Run `rlang::last_error()` to see where the error occurred.
关于如何实现这个的任何想法?
你不能只传入一个字符串。字符串与表达式不同。一种方法是使用 purrr
和 rlang
构建表达式,然后将其注入 select
library(purrr)
library(rlang)
query <- map2(
map(x, ~expr(contains(!!.x))),
map(names(x), ~expr(contains(!!.x))),
~expr((!!.x & !!.y))) %>%
reduce(~expr(!!.x | !!.y))
dplyr::select(df, !!query)
虽然如果您真的想将代码构建为字符串,那么您只需要先使用 rlang::parse_expr
将该字符串解析为表达式。您只需要在字符串中添加一些引号,使其与您之前使用的代码完全匹配
s <- paste0("(", paste0("contains(\"", names(x),"\") & contains(\"", x, "\")"), ")", collapse = " | ")
dplyr::select(df, !!rlang::parse_expr(s))
@MrFlick 的回答是构建 tidyselect 表达式的方法。
但是,由于您已经以编程方式构建选择:而不是 tidyselect
表达式,只做相应的计算可能会更容易
自己起名字。
library(purrr)
set.seed(42)
df <- data.frame(
grey_dog = runif(n = 5),
white_bear = runif(n = 5),
blue_oyster = runif(n = 5),
white_lobster = runif(n = 5),
green_dog = runif(n = 5)
)
x <- list(
c("green", "dog"),
c("white", "bear")
)
sel <- x |>
map(sapply, grepl, names(df)) |>
map(apply, 1, all) |>
reduce(`|`)
df[, sel]
#> white_bear green_dog
#> 1 0.5190959 0.90403139
#> 2 0.7365883 0.13871017
#> 3 0.1346666 0.98889173
#> 4 0.6569923 0.94666823
#> 5 0.7050648 0.08243756
或者,您可以使用 map()
在选择函数中应用助手,然后执行设置
对应于 &
和 |
的操作步骤类似:
df |>
dplyr::select(
x |>
map(map, contains) |>
map(reduce, intersect) |>
reduce(union)
)
#> green_dog white_bear
#> 1 0.90403139 0.5190959
#> 2 0.13871017 0.7365883
#> 3 0.98889173 0.1346666
#> 4 0.94666823 0.6569923
#> 5 0.08243756 0.7050648
我需要 select 在多个表达式上动态变量。考虑以下示例:
library(tidyverse)
set.seed(42)
df <- tibble(
grey_dog = runif(n = 69),
white_bear = runif(n = 69),
blue_oyster = runif(n = 69),
white_lobster = runif(n = 69),
green_dog = runif(n = 69)
)
df %>%
dplyr::select(
(contains("dog") & contains("green")) |
(contains("white") & contains("bear"))
)
而不是显式 selecting,我有包含信息的向量,我想将我的 selection 基于:
x <- c(green = "dog", white = "bear")
所以我希望连接一个可以用作整洁的字符串-select:
s <- paste0("(", paste0("contains(", names(x),") & contains(", x, ")"), ")", collapse = " | ")
dplyr::select(df, s)
这失败了:
Error: Can't subset columns that don't exist.
x Column `(contains(green) & contains(dog)) | (contains(white) & contains(bear))` doesn't exist.
Run `rlang::last_error()` to see where the error occurred.
关于如何实现这个的任何想法?
你不能只传入一个字符串。字符串与表达式不同。一种方法是使用 purrr
和 rlang
构建表达式,然后将其注入 select
library(purrr)
library(rlang)
query <- map2(
map(x, ~expr(contains(!!.x))),
map(names(x), ~expr(contains(!!.x))),
~expr((!!.x & !!.y))) %>%
reduce(~expr(!!.x | !!.y))
dplyr::select(df, !!query)
虽然如果您真的想将代码构建为字符串,那么您只需要先使用 rlang::parse_expr
将该字符串解析为表达式。您只需要在字符串中添加一些引号,使其与您之前使用的代码完全匹配
s <- paste0("(", paste0("contains(\"", names(x),"\") & contains(\"", x, "\")"), ")", collapse = " | ")
dplyr::select(df, !!rlang::parse_expr(s))
@MrFlick 的回答是构建 tidyselect 表达式的方法。
但是,由于您已经以编程方式构建选择:而不是 tidyselect 表达式,只做相应的计算可能会更容易 自己起名字。
library(purrr)
set.seed(42)
df <- data.frame(
grey_dog = runif(n = 5),
white_bear = runif(n = 5),
blue_oyster = runif(n = 5),
white_lobster = runif(n = 5),
green_dog = runif(n = 5)
)
x <- list(
c("green", "dog"),
c("white", "bear")
)
sel <- x |>
map(sapply, grepl, names(df)) |>
map(apply, 1, all) |>
reduce(`|`)
df[, sel]
#> white_bear green_dog
#> 1 0.5190959 0.90403139
#> 2 0.7365883 0.13871017
#> 3 0.1346666 0.98889173
#> 4 0.6569923 0.94666823
#> 5 0.7050648 0.08243756
或者,您可以使用 map()
在选择函数中应用助手,然后执行设置
对应于 &
和 |
的操作步骤类似:
df |>
dplyr::select(
x |>
map(map, contains) |>
map(reduce, intersect) |>
reduce(union)
)
#> green_dog white_bear
#> 1 0.90403139 0.5190959
#> 2 0.13871017 0.7365883
#> 3 0.98889173 0.1346666
#> 4 0.94666823 0.6569923
#> 5 0.08243756 0.7050648