什么!! R中的运算符均值
What does !! operator mean in R
谁能解释一下,我们需要来自 rlang
的 !!
、!!!
或 {{}}
运算符有什么用?我试图学习 more 关于准报价但没有得到任何东西。
我已经在 Stack 上发表了几篇关于 curly-curly operator 的帖子,并且了解到我们在将数据帧的变量(或我们对象的其他子对象)传递给函数时使用 {{
。但是在阅读 quote/unquote 之后,我对所有这些运算符及其用法完全感到困惑。
为什么我们需要它,为什么有些函数没有它就不能读取参数,最后,它们是如何工作的?
如果你用我都能理解的最简单的方式回答,我将不胜感激(也许有例子?)。
!!
和 {{
运算符是占位符,用于将变量标记为已被引用。通常仅当您打算使用 tidyverse
进行编程时才需要它们。
tidyverse
喜欢利用 NSE(非标准评估)来减少重复量。最常见的应用是针对 "data.frame"
class,其中 expressions/symbols 在搜索其他范围之前在 data.frame 的上下文中进行评估。
为了让它工作,一些特殊函数(比如包 dplyr
中的)有引用的参数。引用一个表达式,是为了保存组成表达式的符号并防止求值(在 tidyverse
的上下文中,他们使用“quosures”,除了它包含对环境的引用外,它类似于引用表达式表达了)。
虽然 NSE 非常适合交互式使用,但它显然更难编程。
让我们考虑 dplyr::select
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
iris <- as_tibble(iris)
my_select <- function(.data, col) {
select(.data, col)
}
select(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
my_select(iris, Species)
#> Error: object 'Species' not found
我们遇到错误,因为在my_select
范围内
col
参数使用标准评估进行评估,并且
找不到名为 Species
.
的变量
如果我们尝试在全局环境中创建一个变量,我们会看到函数
有效 - 但它不符合 tidyverse
的启发式方法。实际上,
他们会出示一张纸条,告知您这是不明确的使用。
Species <- "Sepal.Width"
my_select(iris, Species)
#> Note: Using an external vector in selections is ambiguous.
#> ℹ Use `all_of(col)` instead of `col` to silence this message.
#> ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
#> This message is displayed once per session.
#> # A tibble: 150 × 1
#> Sepal.Width
#> <dbl>
#> 1 3.5
#> 2 3
#> 3 3.2
#> 4 3.1
#> 5 3.6
#> 6 3.9
#> 7 3.4
#> 8 3.4
#> 9 2.9
#> 10 3.1
#> # … with 140 more rows
为了解决这个问题,我们需要
以防止使用 enquo()
进行评估并使用 !!
取消引用或仅使用 {{
.
my_select2 <- function(.data, col) {
col_quo <- enquo(col)
select(.data, !!col_quo) #attempting to find whatever symbols were passed to `col` arugment
}
#' `{{` enables the user to skip using the `enquo()` step.
my_select3 <- function(.data, col) {
select(.data, {{col}})
}
my_select2(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
my_select3(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
总而言之,如果您尝试以编程方式应用 NSE,您实际上只需要 !!
和 {{
或对该语言进行某种类型的编程。
!!!
用于将某种类型的 list/vector 拼接到某些引用表达式的参数中。
library(rlang)
quo_let <- quo(paste(!!!LETTERS))
quo_let
#> <quosure>
#> expr: ^paste("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
#> "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
#> "Z")
#> env: global
eval_tidy(quo_let)
#> [1] "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
由 reprex package (v2.0.1)
于 2021-08-30 创建
非标准评估 (NSE) 通常与 tidyverse/dplyr 一起使用,但大多数人在加载包时每天都会遇到它。
a <- "rlang"
print(a) # Standard evaluation: the expression a is replace by its value
# [1] "rlang"
library(a) # Non-standard evaluation: the expression a is used as-is
# Error in library(a) : there is no package called ‘a’
那么,如何加载一个动态指定的包呢?在这里,我们将使用准报价进行演示。 (在实际代码中,我建议改为 library(a, character.only=TRUE)
。)
在基础 R 中,您可以使用 bquote()
动态构造表达式,然后对其求值。
myexpr <- bquote(library(.(a))) # myexpr will now be library("rlang")
eval(myexpr) # rlang is now loaded
rlang
提供了额外的工具来操作表达式。总的来说,它们比基本的 R 工具更能表达。 !!
的行为与上述类似:
myexpr <- rlang::expr(library(!!a)) # Same as above, myexpr is now library("rlang")
您可以使用 rlang::expr
和 !!
来构造任何表达式以供将来计算。
x <- rlang::expr(mtcars)
y <- rlang::expr(mpg > 30)
z <- rlang::expr(disp)
rlang::expr(subset(!!x, !!y, !!z)) # Constructs subset(mtcars, mpg > 30, disp)
当你有很多参数时,你可以将它们放在一个列表中并使用 !!!
快捷方式。上面的表达式可以用
复制
l <- rlang::exprs(mtcars, mpg > 30, disp) # Note the s on exprs
rlang::expr(subset(!!!l)) # Also builds subset(mtcars, mpg > 30, disp)
{{
运算符是解释起来最复杂的一个,需要引入quosures。
R 中的表达式是 first-class 对象,这意味着它们可以传递给函数,由函数返回等。但是,使用 rlang::expr
创建的表达式总是在它们的立即数中求值语境。考虑一下,
a <- 10
x <- rlang::expr(a+5)
f <- function(y) {
a <- 5
eval(y)
}
f(x) # What does this return?
即使表达式 x
捕获 a+5
,a
的值也会在计算表达式之前发生变化。 Quosures 捕获表达式和定义它们的环境。该环境始终用于计算该表达式。
a <- 10
x <- rlang::quo(a+5) # Quosure = expression + environment where a == 10
f <- function(y) {
a <- 5
eval_tidy(y) # Instead of simple eval()
}
f(x) # 15 = 10 + 5
通过使用 expr
和 quo
:
的 en-
版本,可以将捕获表达式或 quosure 移动到函数内部
f <- function(y) {
a <- 5
eval(rlang::enexpr(y))
}
g <- function(y) {
a <- 5
eval_tidy(rlang::enquo(y))
}
允许用户将表达式直接传递给函数
a <- 10
f(a*4) # 20 = 5*4, because f captures expressions, and a is overwritten
g(a*4) # 40 = 10*4, because g captures quosures
综上所述,{{x}}
只是 !!enquo(x)
的 shorthand 符号。
谁能解释一下,我们需要来自 rlang
的 !!
、!!!
或 {{}}
运算符有什么用?我试图学习 more 关于准报价但没有得到任何东西。
我已经在 Stack 上发表了几篇关于 curly-curly operator 的帖子,并且了解到我们在将数据帧的变量(或我们对象的其他子对象)传递给函数时使用 {{
。但是在阅读 quote/unquote 之后,我对所有这些运算符及其用法完全感到困惑。
为什么我们需要它,为什么有些函数没有它就不能读取参数,最后,它们是如何工作的?
如果你用我都能理解的最简单的方式回答,我将不胜感激(也许有例子?)。
!!
和 {{
运算符是占位符,用于将变量标记为已被引用。通常仅当您打算使用 tidyverse
进行编程时才需要它们。
tidyverse
喜欢利用 NSE(非标准评估)来减少重复量。最常见的应用是针对 "data.frame"
class,其中 expressions/symbols 在搜索其他范围之前在 data.frame 的上下文中进行评估。
为了让它工作,一些特殊函数(比如包 dplyr
中的)有引用的参数。引用一个表达式,是为了保存组成表达式的符号并防止求值(在 tidyverse
的上下文中,他们使用“quosures”,除了它包含对环境的引用外,它类似于引用表达式表达了)。
虽然 NSE 非常适合交互式使用,但它显然更难编程。
让我们考虑 dplyr::select
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
iris <- as_tibble(iris)
my_select <- function(.data, col) {
select(.data, col)
}
select(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
my_select(iris, Species)
#> Error: object 'Species' not found
我们遇到错误,因为在my_select
范围内
col
参数使用标准评估进行评估,并且
找不到名为 Species
.
如果我们尝试在全局环境中创建一个变量,我们会看到函数
有效 - 但它不符合 tidyverse
的启发式方法。实际上,
他们会出示一张纸条,告知您这是不明确的使用。
Species <- "Sepal.Width"
my_select(iris, Species)
#> Note: Using an external vector in selections is ambiguous.
#> ℹ Use `all_of(col)` instead of `col` to silence this message.
#> ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
#> This message is displayed once per session.
#> # A tibble: 150 × 1
#> Sepal.Width
#> <dbl>
#> 1 3.5
#> 2 3
#> 3 3.2
#> 4 3.1
#> 5 3.6
#> 6 3.9
#> 7 3.4
#> 8 3.4
#> 9 2.9
#> 10 3.1
#> # … with 140 more rows
为了解决这个问题,我们需要
以防止使用 enquo()
进行评估并使用 !!
取消引用或仅使用 {{
.
my_select2 <- function(.data, col) {
col_quo <- enquo(col)
select(.data, !!col_quo) #attempting to find whatever symbols were passed to `col` arugment
}
#' `{{` enables the user to skip using the `enquo()` step.
my_select3 <- function(.data, col) {
select(.data, {{col}})
}
my_select2(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
my_select3(iris, Species)
#> # A tibble: 150 × 1
#> Species
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa
#> 7 setosa
#> 8 setosa
#> 9 setosa
#> 10 setosa
#> # … with 140 more rows
总而言之,如果您尝试以编程方式应用 NSE,您实际上只需要 !!
和 {{
或对该语言进行某种类型的编程。
!!!
用于将某种类型的 list/vector 拼接到某些引用表达式的参数中。
library(rlang)
quo_let <- quo(paste(!!!LETTERS))
quo_let
#> <quosure>
#> expr: ^paste("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
#> "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
#> "Z")
#> env: global
eval_tidy(quo_let)
#> [1] "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
由 reprex package (v2.0.1)
于 2021-08-30 创建非标准评估 (NSE) 通常与 tidyverse/dplyr 一起使用,但大多数人在加载包时每天都会遇到它。
a <- "rlang"
print(a) # Standard evaluation: the expression a is replace by its value
# [1] "rlang"
library(a) # Non-standard evaluation: the expression a is used as-is
# Error in library(a) : there is no package called ‘a’
那么,如何加载一个动态指定的包呢?在这里,我们将使用准报价进行演示。 (在实际代码中,我建议改为 library(a, character.only=TRUE)
。)
在基础 R 中,您可以使用 bquote()
动态构造表达式,然后对其求值。
myexpr <- bquote(library(.(a))) # myexpr will now be library("rlang")
eval(myexpr) # rlang is now loaded
rlang
提供了额外的工具来操作表达式。总的来说,它们比基本的 R 工具更能表达。 !!
的行为与上述类似:
myexpr <- rlang::expr(library(!!a)) # Same as above, myexpr is now library("rlang")
您可以使用 rlang::expr
和 !!
来构造任何表达式以供将来计算。
x <- rlang::expr(mtcars)
y <- rlang::expr(mpg > 30)
z <- rlang::expr(disp)
rlang::expr(subset(!!x, !!y, !!z)) # Constructs subset(mtcars, mpg > 30, disp)
当你有很多参数时,你可以将它们放在一个列表中并使用 !!!
快捷方式。上面的表达式可以用
l <- rlang::exprs(mtcars, mpg > 30, disp) # Note the s on exprs
rlang::expr(subset(!!!l)) # Also builds subset(mtcars, mpg > 30, disp)
{{
运算符是解释起来最复杂的一个,需要引入quosures。
R 中的表达式是 first-class 对象,这意味着它们可以传递给函数,由函数返回等。但是,使用 rlang::expr
创建的表达式总是在它们的立即数中求值语境。考虑一下,
a <- 10
x <- rlang::expr(a+5)
f <- function(y) {
a <- 5
eval(y)
}
f(x) # What does this return?
即使表达式 x
捕获 a+5
,a
的值也会在计算表达式之前发生变化。 Quosures 捕获表达式和定义它们的环境。该环境始终用于计算该表达式。
a <- 10
x <- rlang::quo(a+5) # Quosure = expression + environment where a == 10
f <- function(y) {
a <- 5
eval_tidy(y) # Instead of simple eval()
}
f(x) # 15 = 10 + 5
通过使用 expr
和 quo
:
en-
版本,可以将捕获表达式或 quosure 移动到函数内部
f <- function(y) {
a <- 5
eval(rlang::enexpr(y))
}
g <- function(y) {
a <- 5
eval_tidy(rlang::enquo(y))
}
允许用户将表达式直接传递给函数
a <- 10
f(a*4) # 20 = 5*4, because f captures expressions, and a is overwritten
g(a*4) # 40 = 10*4, because g captures quosures
综上所述,{{x}}
只是 !!enquo(x)
的 shorthand 符号。