!!和!!as.symbol之间的区别是什么?

huangapple go评论79阅读模式
英文:

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(&quot;colname&quot;)) < 3) |> invisible()
##  chr "a"
df |> filter(look(!!colname) == "a") |> invisible()
##  chr "a"
df |> filter(look(!!&quot;colname&quot;) == "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(&quot;colname&quot;)) < 3) |> invisible()
##  num [1:5] 11 12 13 14 1
df2 |> filter(look(!!colname) == "a") |> invisible()
##  chr "a"
df2 |> filter(look(!!&quot;colname&quot;) == "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 the a column as a vector

  • !!colname returns the character string &quot;a&quot;

  • !!"colname" returns the character string &quot;colname&quot;

However, !!as.symbol(&quot;colname&quot;) acts differently depending on whether colname is a column name or not.

df &lt;- data.frame(a=1:5,b=6:10);
colname &lt;- &quot;a&quot;
look &lt;- function(x) { str(x); x }

df |&gt; filter(look(!!as.symbol(colname)) &lt; 3) |&gt; invisible()
##  int [1:5] 1 2 3 4 5
df |&gt; filter(look(!!as.symbol(&quot;colname&quot;)) &lt; 3) |&gt; invisible()
##  chr &quot;a&quot;
df |&gt; filter(look(!!colname) == &quot;a&quot;) |&gt; invisible()
##  chr &quot;a&quot;
df |&gt; filter(look(!!&quot;colname&quot;) == &quot;a&quot;) |&gt; invisible()
##  chr &quot;colname&quot;

####

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

答案2

得分: 2

如果您查看 !! 的帮助页面 (help("!!")),这将重定向您到 rlang 包中的 inject 函数。通过组合 injectsubstitute 函数,您可以逻辑地重建要评估的表达式。

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

考虑您的示例,在替代 colname 后,以下表达式展示了两种情况之间的差异:

colname <- "a"

### 使用 !!colname &lt; 3) ###
rlang::inject(substitute(
  df |> 
  filter(!!colname &lt; 3)
))
# filter(df, "a" &lt; 3)                                 # 包含字符串 "a"


### 使用 !!as.symbol(colname) &lt; 3) ###
rlang::inject(substitute(
  df |> 
  filter(!!as.symbol(colname) &lt; 3)
))
# filter(df, a &lt; 3)                                   # 包含变量 a
英文:

If you look at the help page of !! (help(&quot;!!&quot;)), 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 &lt; 3), where colname = &quot;a&quot;, your are checking if the string &quot;a&quot; is lower than 3 while with !!as.symbol(colname) &lt; 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 &lt;- &quot;a&quot;

### Using !!colname &lt; 3) ###
rlang::inject(substitute(
  df |&gt; 
	filter(!!colname &lt; 3)
))
# filter(df, &quot;a&quot; &lt; 3)                                 # contains a string &quot;a&quot;


### Using !!as.symbol(colname) &lt; 3) ###
rlang::inject(substitute(
  df |&gt; 
	filter(!!as.symbol(colname) &lt; 3)
))
# filter(df, a &lt; 3)                                   # contains a variable a

huangapple
  • 本文由 发表于 2023年7月23日 20:12:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76748172.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定