"!!"和"!!as.symbol"有什么区别?

5

考虑这个例子:

library(dplyr)
df <- data.frame(a=1:5,b=6:10)

> df
  a  b
1 1  6
2 2  7
3 3  8
4 4  9
5 5 10

colname <- "a"
df |> filter(!!as.symbol(colname)<3)

  a b
1 1 6
2 2 7

df |> filter(!!colname<3)
[1] a b
<0 rows> (or 0-length row.names)

应用!!到字符串而不是符号,会实现什么效果?为什么它返回一个空数据框而不是报错?
2个回答

6
让我们首先扩展一下问题中的示例,使用dfdf2,其中df2df加上一个名为colname的额外列。
无论colname是否是一列:
  • !!as.symbol(colname)a列的值返回为向量

  • !!colname返回字符字符串"a"

  • !!"colname"返回字符字符串"colname"

然而,!!as.symbol("colname")的行为取决于colname是否是列名。
df <- data.frame(a=1:5,b=6:10);
colname <- "a"
look <- function(x) { str(x); x }

df |> filter(look(!!as.symbol(colname)) < 3) |> invisible()
##  int [1:5] 1 2 3 4 5
df |> filter(look(!!as.symbol("colname")) < 3) |> invisible()
##  chr "a"
df |> filter(look(!!colname) == "a") |> invisible()
##  chr "a"
df |> filter(look(!!"colname") == "a") |> invisible()
##  chr "colname"

####

df2 <- data.frame(a=1:5,b=6:10,colname=c(11:14, 1));
colname <- "a"
look <- function(x) { str(x); x }
 
df2 |> filter(look(!!as.symbol(colname)) < 3) |> invisible()
##  int [1:5] 1 2 3 4 5
df2 |> filter(look(!!as.symbol("colname")) < 3) |> invisible()
##  num [1:5] 11 12 13 14 1
df2 |> filter(look(!!colname) == "a") |> invisible()
##  chr "a"
df2 |> filter(look(!!"colname") == "a") |> invisible()
##  chr "colname"

我仍然想知道,当 colname <- 2 时,dplyr 如何知道从 select(!!colname) 返回第二列(数值2),而基本的 R 在选择 df[!!colname] 时返回所有列(逻辑 TRUE,将 !! 视为双重否定)。 - I_O
1
首先,选择函数会使用tidyselect包中的eval_select来评估其参数。而Base则不会这样做。 - G. Grothendieck
在上述中使用 |> invisible() 的目的是什么? - Chris
以上的代码中使用了 |> invisible() 是用来做什么的? - undefined

2

如果你查看!!的帮助页面(help("!!")),这将会将你重定向到包rlang中的函数inject。通过结合injectsubstitute函数,你可以逻辑上重建要进行求值的表达式。

使用!!colname < 3,其中colname = "a",你正在检查字符串"a"是否小于3,而使用!!as.symbol(colname) < 3,你正在检查名为a的变量是否小于3。

考虑你的例子,在替代colname之后,以下表达式显示了两种情况之间的区别:

colname <- "a"

### Using !!colname < 3) ###
rlang::inject(substitute(
  df |> 
    filter(!!colname < 3)
))
# filter(df, "a" < 3)                                 # contains a string "a"


### Using !!as.symbol(colname) < 3) ###
rlang::inject(substitute(
  df |> 
    filter(!!as.symbol(colname) < 3)
))
# filter(df, a < 3)                                   # contains a variable a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接