如果我想让类似 dplyr 的函数能够同时与 NSE 和 SE 一起使用,如何解决 {{}} 和 all_off() 的问题?

问题描述

我想写一个函数,它会同时使用列的符号名称和作为变量(字符串)传递的名称

让我给你举个例子:

数据:

> ( d <- data.frame(A=1:3,B=3:1) )
  A B
1 1 3
2 2 2
3 3 1

现在我的功能

fn <- function(data,cols) {
  return(data %>% mutate(across({{cols}},~. * 2)))
}

它适用于:

A) 符号名称

> d %>% fn(cols = A)
  A B
1 2 3
2 4 2
3 6 1

> d %>% fn(cols = B)
  A B
1 1 6
2 2 4
3 3 2

> d %>% fn(cols = c(A,B))
  A B
1 2 6
2 4 4
3 6 2

B) 作为字符串传递的名称

> column <- "A"
> d %>% fn(cols = column)
  A B
1 2 3
2 4 2
3 6 1

> d %>% fn(cols = c("A","B"))
  A B
1 2 6
2 4 4
3 6 2

到目前为止,一切都很好!

现在,当我提供大于 1 列的外部向量时,它会发出警告。

> d %>% fn(cols = columns)
Note: Using an external vector in selections is ambiguous.
i Use `all_of(columns)` instead of `columns` to silence this message.
i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
  A B
1 2 6
2 4 4
3 6 2

所以我添加了 all_of 函数,它适用于字符串:

fn <- function(data,cols) {
  return(data %>% mutate(across(all_of({{cols}}),~. * 2)))
}

> d %>% fn(cols = columns)
  A B
1 2 6
2 4 4
3 6 2

但是当我传递符号名称时抛出错误

> d %>% fn(cols = A)

 Error: Problem with `mutate()` input `..1`.
x object 'A' not found
i Input `..1` is `across(all_of(A),~. * 2)`.
Run `rlang::last_error()` to see where the error occurred. > d %>% fn(cols = B)

> d %>% fn(cols = c(A,B))

 Error: Problem with `mutate()` input `..1`.
x object 'A' not found
i Input `..1` is `across(all_of(c(A,B)),~. * 2)`.
Run `rlang::last_error()` to see where the error occurred. 

如何解决这个问题,以启用两种方法并避免警告?

解决方法

我的建议是保留您的原始实施和随之而来的警告,因为情况确实模棱两可。考虑:

d <- data.frame(A=1:3,B=3:1,columns=4:6)  # Note the new column named columns
columns <- c("A","B")
d %>% fn(cols = columns)                    # Which `columns` should this use?

您的函数的用户然后可以通过自己使用 all_of() 来解决歧义,您可以在函数的帮助页面中记录这一点。

d %>% fn(cols = all_of(columns))     # works without a warning

编辑: 虽然我推荐上述方法,但另一种方法是检查调用环境中变量的存在。如果变量存在,假设它包含列名并在 all_of() 中使用它;否则,假设列名按原样提供:

fn <- function(data,cols) {
  varExist <- rlang::enexpr(cols) %>% 
    rlang::expr_deparse() %>%
    exists(envir=rlang::caller_env())
  
  if(varExist)
    data %>% mutate( across(all_of(cols),~. *2) )
  else
    data %>% mutate( across({{cols}},~. * 2) )
}

rm(A)              # Ensure there is no variable called A
d %>% fn(cols=A)   # Mutate will operate on column A only

A <- c("A","B")    # A now contains column names
d %>% fn(cols=A)   # Mutate will operate on A and B

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...