将 tidy select 参数转换为字符向量
Converting tidy select arguments to character vector
我正在努力将整洁的 select 样式函数参数转换为字符向量。
f <- function(d, cols1, cols2) {
<do something useful with d>
<some magic>
}
f(iris, c(Sepal.Length, Species), Petal.Width)
# [1] "Sepal.Length" "Species" "Petal.Width"
这是一个激励人心的例子。
假设我有一个函数可以创建两个 data.frame
并将它们连接起来。其中一个 data.frame
的计算时间很长,所以我想有一个选项来提供预先计算的 data.frame.
这里我使用的是使用 embrace 的 tidy evaluation。看一个最小的例子:
my_func <- function(df, cols1, cols2, df2=NULL) {
df1 <- df %>%
do_something(across({{cols1}})
if (!is.null(df2) {
# 1. here I would like to check column names of df2
} else {
df2 <- df %>%
do_something_very_slow(across({{cols2}})
}
# 2. here I would like to provide by=c(cols1, cols2)
full_join(df1, df2)
}
# I am using tidy select to pass column names to the function.
result <- my_func(df, c(a, b), c)
现在我想对这段代码做两处改进。
- 在 #1 中,我想验证 precomputed_df 是否具有所需的所有列(即:a、b 和 c)。直接的方法是将 c(col1, col2) 转换为字符向量。但是当然这会通过一个错误。我看到的其他替代方案是尝试
select()
和 tryCatch()
,但这看起来很丑。
- 在#2
full_join()
中将通过一条消息“按列 XXX 加入”。避免这种情况的唯一方法是提供列名 by=c('a', 'b', 'c')
的显式字符向量。但为此我又需要一个字符向量。丑陋的解决方法是 intersect(names(df1), names(df2))
或 suppressMessages()
。当确定 df1 和 df2 中没有额外的列时,这可能会起作用。另一种相当丑陋的方法是使用 names(select(df1, c({{cols1}}, {{cols2}}))
.
有没有更优雅的方法将cols1
和cols2
转换为字符向量?
也许我误解了你的问题,但在 tidyselect 上下文中获取字符向量的方法是 all_of()
:
across(all_of(cols1))
f <- function(d, cols1, cols2) {
<do something useful with d>
cols1_c <- tidyselect::eval_select(rlang::enquo(cols1), d)
cols2_c <- tidyselect::eval_select(rlang::enquo(cols2), d)
unique(names(d)[c(cols1_c, cols2_c)])
}
f(iris, c(Sepal.Length, Species), Petal.Width)
# [1] "Sepal.Length" "Species" "Petal.Width"
我正在努力将整洁的 select 样式函数参数转换为字符向量。
f <- function(d, cols1, cols2) {
<do something useful with d>
<some magic>
}
f(iris, c(Sepal.Length, Species), Petal.Width)
# [1] "Sepal.Length" "Species" "Petal.Width"
这是一个激励人心的例子。
假设我有一个函数可以创建两个 data.frame
并将它们连接起来。其中一个 data.frame
的计算时间很长,所以我想有一个选项来提供预先计算的 data.frame.
这里我使用的是使用 embrace 的 tidy evaluation。看一个最小的例子:
my_func <- function(df, cols1, cols2, df2=NULL) {
df1 <- df %>%
do_something(across({{cols1}})
if (!is.null(df2) {
# 1. here I would like to check column names of df2
} else {
df2 <- df %>%
do_something_very_slow(across({{cols2}})
}
# 2. here I would like to provide by=c(cols1, cols2)
full_join(df1, df2)
}
# I am using tidy select to pass column names to the function.
result <- my_func(df, c(a, b), c)
现在我想对这段代码做两处改进。
- 在 #1 中,我想验证 precomputed_df 是否具有所需的所有列(即:a、b 和 c)。直接的方法是将 c(col1, col2) 转换为字符向量。但是当然这会通过一个错误。我看到的其他替代方案是尝试
select()
和tryCatch()
,但这看起来很丑。 - 在#2
full_join()
中将通过一条消息“按列 XXX 加入”。避免这种情况的唯一方法是提供列名by=c('a', 'b', 'c')
的显式字符向量。但为此我又需要一个字符向量。丑陋的解决方法是intersect(names(df1), names(df2))
或suppressMessages()
。当确定 df1 和 df2 中没有额外的列时,这可能会起作用。另一种相当丑陋的方法是使用names(select(df1, c({{cols1}}, {{cols2}}))
.
有没有更优雅的方法将cols1
和cols2
转换为字符向量?
也许我误解了你的问题,但在 tidyselect 上下文中获取字符向量的方法是 all_of()
:
across(all_of(cols1))
f <- function(d, cols1, cols2) {
<do something useful with d>
cols1_c <- tidyselect::eval_select(rlang::enquo(cols1), d)
cols2_c <- tidyselect::eval_select(rlang::enquo(cols2), d)
unique(names(d)[c(cols1_c, cols2_c)])
}
f(iris, c(Sepal.Length, Species), Petal.Width)
# [1] "Sepal.Length" "Species" "Petal.Width"