关于 tidyeval,`sym()` 做了什么?

What does `sym()` do regarding tidyeval?

library(tidyverse)
input_name <- "birth_year"
input_value <- 19

quo(filter(starwars, !!input_name == !!input_value))       # line 5
quo(filter(starwars, !!sym(input_name) == !!input_value))  # line 6

第5行和第6行有什么区别,以及sym()函数的使用?为什么 sym() 只需要在第 6 行的等式左侧?

sym() 的目的是获取字符串并将其取消引用为符号吗?

第 5 行

<quosure>
  expr: ^filter(data, "birth_year" == 19)
  env:  global

第 6 行

<quosure>
  expr: ^filter(data, birth_year == 19)
  env:  global

在第一种情况下,不会评估列,评估的是字符串。但是,通过转换为 symbol 并计算它,它 returns 列值。 lhs 中需要 sym 因为我们不是要获取字面值,而是要提取列值

根据?sym

sym() creates a symbol from a string and syms() creates a list of symbols from a character vector.

?"!!"

The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.

答案是肯定的,sym()的目的就是把字符串解析成符号。在等式的左侧需要这个的原因可以在 ?filter:

中看到
 ...: Logical predicates defined in terms of the variables in
      ‘.data’. Multiple conditions are combined with ‘&’. Only rows
      where the condition evaluates to ‘TRUE’ are kept.

filter( starwars, "birth_year" == 19 ) 将始终 return 没有结果,因为 string literal "birth_year" 永远不会等于整数文字 19 (它被隐式强制为比较中的字符文字 "19")。通过使用 sym,您可以有效地将字符串解析为一个符号,从而强制 filter 查看数据框 starwars 中名为 birth_year 的列,而不是文字字符串 "birth_year".

相反,你不需要等式右边的sym(),因为[=]中没有第19列21=],而您对实际文字值 19 感兴趣。如果您要比较数据框中的两列,那么您需要在等式的两边使用 sym()。例如,

name1 <- "skin_color"
name2 <- "eye_color"
filter( starwars, !!sym(name1) == !!sym(name2) )
# # A tibble: 6 x 13
#   name  height  mass hair_color skin_color eye_color birth_year gender homeworld
#   <chr>  <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr>  <chr>    
# 1 Wick…     88    20 brown      brown      brown              8 male   Endor    
# 2 Jar …    196    66 none       orange     orange            52 male   Naboo    
# 3 Eeth…    171    NA black      brown      brown             NA male   Iridonia 
# 4 Mas …    196    NA none       blue       blue              NA male   Champala 
# ...