将 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. 在 #1 中,我想验证 precomputed_df 是否具有所需的所有列(即:a、b 和 c)。直接的方法是将 c(col1, col2) 转换为字符向量。但是当然这会通过一个错误。我看到的其他替代方案是尝试 select()tryCatch(),但这看起来很丑。
  2. 在#2 full_join() 中将通过一条消息“按列 XXX 加入”。避免这种情况的唯一方法是提供列名 by=c('a', 'b', 'c') 的显式字符向量。但为此我又需要一个字符向量。丑陋的解决方法是 intersect(names(df1), names(df2))suppressMessages()。当确定 df1 和 df2 中没有额外的列时,这可能会起作用。另一种相当丑陋的方法是使用 names(select(df1, c({{cols1}}, {{cols2}})).

有没有更优雅的方法将cols1cols2转换为字符向量?

也许我误解了你的问题,但在 tidyselect 上下文中获取字符向量的方法是 all_of():

across(all_of(cols1))

深入tidyselect documentation后:

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"