英文:
What is the difference between !! and !!as.symbol?
问题
将!!应用于字符串而不是符号会导致R语言将字符串解释为变量名,然后尝试过滤数据框中不满足条件的行。在你的示例中,colname是一个字符串,它包含变量名"a"。所以,当你执行以下操作时:
df |> filter(!!colname<3)
R语言将尝试筛选数据框df中"a"列中小于3的行。但是,在数据框中不存在名为"a"的列,因此条件不成立,导致返回一个空数据框。这并不会引发错误,因为R语言允许在筛选中使用不存在的列名,只是结果将是空的。
另一方面,如果你应用!!到一个符号,R语言将把符号解释为实际的变量,然后执行筛选操作。在你的示例中,!!as.symbol(colname)将"a"解释为实际的变量名,并且筛选操作将根据该变量的值来过滤数据框,因此它返回了满足条件的行。
英文:
Consider this example:
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)
What does applying !! to a string rather than a symbol achieves, and why does it return an empty dataframe instead of an error?
答案1
得分: 6
让我们首先扩展一下问题中的 df 示例,然后是 df2,它是 df 加上一个名为 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"
英文:
Let us expand the example first with the df in the question and then df2 which is df plus an extra column called colname.
Whether or not colname is a column:
-
!!as.symbol(colname)returns the value of theacolumn as a vector -
!!colnamereturns the character string"a" -
!!"colname" returns the character string
"colname"
However, !!as.symbol("colname") acts differently depending on whether colname is a column name or not.
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"
答案2
得分: 2
如果您查看 !! 的帮助页面 (help("!!")),这将重定向您到 rlang 包中的 inject 函数。通过组合 inject 和 substitute 函数,您可以逻辑地重建要评估的表达式。
使用 !!colname < 3),其中 colname = "a",您正在检查字符串 "a" 是否小于 3,而使用 !!as.symbol(colname) < 3),您正在检查名为 a 的变量是否小于 3。
考虑您的示例,在替代 colname 后,以下表达式展示了两种情况之间的差异:
colname <- "a"
### 使用 !!colname < 3) ###
rlang::inject(substitute(
df |>
filter(!!colname < 3)
))
# filter(df, "a" < 3) # 包含字符串 "a"
### 使用 !!as.symbol(colname) < 3) ###
rlang::inject(substitute(
df |>
filter(!!as.symbol(colname) < 3)
))
# filter(df, a < 3) # 包含变量 a
英文:
If you look at the help page of !! (help("!!")), this would redirect you to the function inject in the package rlang. And by combining inject and subsititute functions, you can logically rebuild the expression that is evaluated.
Using !!colname < 3), where colname = "a", your are checking if the string "a" is lower than 3 while with !!as.symbol(colname) < 3) you are checking if the variable named a is lower than 3.
Considering your example, the following expressions show the difference between the two cases after colname is substituted:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论