Tidyeval quo vs enquo
Tidyeval quo vs enquo
我偶然发现了这种行为,但不是很理解。有人可以解释一下吗?
我编写了以下函数,但出现了以下错误:
> MyFilter <- function(data, filtersVector) {
filtersVector <- quo(filtersVector)
result <- data %>% filter(Species %in% !!filtersVector)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Error in filter_impl(.data, quo) :
Evaluation error: 'match' requires vector arguments.
但是,如果我按以下方式修改它,它会按预期工作:
> MyFilter <- function(data, filtersVector) {
otherName <- quo(filtersVector)
result <- data %>% filter(Species %in% !!otherName)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
我也意识到在一个函数中我应该使用 enqou
来代替并且它工作正常。
> MyFilter <- function(data, filtersVector) {
filtersVector<- enquo(filtersVector)
result <- data %>% filter(Species %in% !!filtersVector)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
不过,我仍然对上述行为感到困惑,如有任何解释,我们将不胜感激。
当我们传递带引号的向量字符串而不是不带引号的
时,我们需要使用 rlang
中的 syms
MyFilter <- function(data, filtersVector) {
filtersVector <- rlang::syms(filtersVector)
data %>%
filter(Species %in% !!filtersVector)
}
out <- MyFilter(iris, c("setosa", "virginica"))
dim(out)
#[1] 100 5
TLDR:在第一个版本中,您创建了一个自引用(指向自身的符号)。其他版本可以工作,但实际上您不需要在此处使用 quosures 或捕获参数,因为您没有引用数据框列。这也解释了为什么 quo()
和 enquo()
版本工作相同。您可以以正常方式传递参数,无需任何引号,但使用 !!
取消引号以避免任何数据屏蔽错误仍然是个好主意。
您可以在 filter()
调用周围使用 qq_show()
来发现语法差异:
MyFilter <- function(data, filtersVector) {
filtersVector <- quo(filtersVector)
rlang::qq_show(
result <- data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris, c("setosa", "virginica"))
#> result <- data %>% filter(Species %in% (^filtersVector))
所以这里我们要求 filter()
找到 Species
与 filtersVector
的元素匹配的行。您的数据框中没有 filtersVector
列,因此它会在 quosure 环境中查找定义。您已经使用 quo()
创建了一个 quosure,它记录了 您的 表达式(在本例中是一个符号 filtersVector
)和 您的环境(你的功能的环境)。所以它查找一个 filtersVector
对象,其中包含一个引用自身的符号。它只被评估一次,所以没有无限循环,但你实际上是在尝试将向量与符号进行比较,这是一个类型错误:
"setosa" %in% quote(filtersVector)
#> Error in match(x, table, nomatch = 0L) :
#> 'match' requires vector arguments
在你的第二次尝试中,你给 quosure 起了另一个名字。它现在可以工作了,因为 filtersVector
,在你的函数环境中,仍然代表传递给它的参数(向量)。
第三次尝试,这次你用enquo()
。 enquo()
不是捕捉你的表情和你的环境,而是捕捉你的功能用户的表情和环境。让我们再次使用 qq_show()
来查看差异:
MyFilter <- function(data, filtersVector) {
filtersVector<- enquo(filtersVector)
rlang::qq_show(
data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris, c("setosa", "virginica"))
#> data %>% filter(Species %in% (^c("setosa", "virginica")))
现在,quosure 包含一个当场创建向量的调用,%in%
完全可以理解。
请注意您实际上并没有引用数据框列。你正在传递向量。这意味着您根本不需要任何 quosure,也不需要捕获传递给参数的表达式。 enquo()
仅对 delay 计算直到最后有用,因此可以在数据帧内进行计算。如果 quo()
和 enquo()
版本产生相同的结果,这是一个很好的迹象,你根本不需要任何引用。由于不需要它们,让我们通过删除等式的等式来简化函数:
MyFilter <- function(data, filtersVector) {
data %>% filter(Species %in% filtersVector)
}
MyFilter(iris, c("setosa", "virginica"))
#> # A tibble: 100 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#> 7 4.6 3.4 1.4 0.3 setosa
#> 8 5 3.4 1.5 0.2 setosa
#> 9 4.4 2.9 1.4 0.2 setosa
#> 10 4.9 3.1 1.5 0.1 setosa
#> # ... with 90 more rows
有效!但是,如果数据框包含一个 filtersVector
列,会发生什么情况呢?它优先于环境中的对象:
iris %>%
mutate(filtersVector = "parasite vector") %>%
MyFilter(c("setosa", "virginica"))
#> # A tibble: 0 x 6
#> # ... with 6 variables: Sepal.Length <dbl>, Sepal.Width <dbl>,
#> # Petal.Length <dbl>, Petal.Width <dbl>, Species <fct>, filtersVector <chr>
所以取消引用仍然是个好主意,因为这样会立即计算向量并将其粘贴在过滤器表达式中。它不能再被列屏蔽。内联显示为 qq_show()
:
MyFilter <- function(data, filtersVector) {
rlang::qq_show(
data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris2, c("setosa", "virginica"))
#> data %>% filter(Species %in% <chr: "setosa", "virginica">)
我偶然发现了这种行为,但不是很理解。有人可以解释一下吗?
我编写了以下函数,但出现了以下错误:
> MyFilter <- function(data, filtersVector) {
filtersVector <- quo(filtersVector)
result <- data %>% filter(Species %in% !!filtersVector)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Error in filter_impl(.data, quo) :
Evaluation error: 'match' requires vector arguments.
但是,如果我按以下方式修改它,它会按预期工作:
> MyFilter <- function(data, filtersVector) {
otherName <- quo(filtersVector)
result <- data %>% filter(Species %in% !!otherName)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
我也意识到在一个函数中我应该使用 enqou
来代替并且它工作正常。
> MyFilter <- function(data, filtersVector) {
filtersVector<- enquo(filtersVector)
result <- data %>% filter(Species %in% !!filtersVector)
result
}
> MyFilter(iris, c("setosa", "virginica"))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
不过,我仍然对上述行为感到困惑,如有任何解释,我们将不胜感激。
当我们传递带引号的向量字符串而不是不带引号的
时,我们需要使用rlang
中的 syms
MyFilter <- function(data, filtersVector) {
filtersVector <- rlang::syms(filtersVector)
data %>%
filter(Species %in% !!filtersVector)
}
out <- MyFilter(iris, c("setosa", "virginica"))
dim(out)
#[1] 100 5
TLDR:在第一个版本中,您创建了一个自引用(指向自身的符号)。其他版本可以工作,但实际上您不需要在此处使用 quosures 或捕获参数,因为您没有引用数据框列。这也解释了为什么 quo()
和 enquo()
版本工作相同。您可以以正常方式传递参数,无需任何引号,但使用 !!
取消引号以避免任何数据屏蔽错误仍然是个好主意。
您可以在 filter()
调用周围使用 qq_show()
来发现语法差异:
MyFilter <- function(data, filtersVector) {
filtersVector <- quo(filtersVector)
rlang::qq_show(
result <- data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris, c("setosa", "virginica"))
#> result <- data %>% filter(Species %in% (^filtersVector))
所以这里我们要求 filter()
找到 Species
与 filtersVector
的元素匹配的行。您的数据框中没有 filtersVector
列,因此它会在 quosure 环境中查找定义。您已经使用 quo()
创建了一个 quosure,它记录了 您的 表达式(在本例中是一个符号 filtersVector
)和 您的环境(你的功能的环境)。所以它查找一个 filtersVector
对象,其中包含一个引用自身的符号。它只被评估一次,所以没有无限循环,但你实际上是在尝试将向量与符号进行比较,这是一个类型错误:
"setosa" %in% quote(filtersVector)
#> Error in match(x, table, nomatch = 0L) :
#> 'match' requires vector arguments
在你的第二次尝试中,你给 quosure 起了另一个名字。它现在可以工作了,因为 filtersVector
,在你的函数环境中,仍然代表传递给它的参数(向量)。
第三次尝试,这次你用enquo()
。 enquo()
不是捕捉你的表情和你的环境,而是捕捉你的功能用户的表情和环境。让我们再次使用 qq_show()
来查看差异:
MyFilter <- function(data, filtersVector) {
filtersVector<- enquo(filtersVector)
rlang::qq_show(
data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris, c("setosa", "virginica"))
#> data %>% filter(Species %in% (^c("setosa", "virginica")))
现在,quosure 包含一个当场创建向量的调用,%in%
完全可以理解。
请注意您实际上并没有引用数据框列。你正在传递向量。这意味着您根本不需要任何 quosure,也不需要捕获传递给参数的表达式。 enquo()
仅对 delay 计算直到最后有用,因此可以在数据帧内进行计算。如果 quo()
和 enquo()
版本产生相同的结果,这是一个很好的迹象,你根本不需要任何引用。由于不需要它们,让我们通过删除等式的等式来简化函数:
MyFilter <- function(data, filtersVector) {
data %>% filter(Species %in% filtersVector)
}
MyFilter(iris, c("setosa", "virginica"))
#> # A tibble: 100 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#> 7 4.6 3.4 1.4 0.3 setosa
#> 8 5 3.4 1.5 0.2 setosa
#> 9 4.4 2.9 1.4 0.2 setosa
#> 10 4.9 3.1 1.5 0.1 setosa
#> # ... with 90 more rows
有效!但是,如果数据框包含一个 filtersVector
列,会发生什么情况呢?它优先于环境中的对象:
iris %>%
mutate(filtersVector = "parasite vector") %>%
MyFilter(c("setosa", "virginica"))
#> # A tibble: 0 x 6
#> # ... with 6 variables: Sepal.Length <dbl>, Sepal.Width <dbl>,
#> # Petal.Length <dbl>, Petal.Width <dbl>, Species <fct>, filtersVector <chr>
所以取消引用仍然是个好主意,因为这样会立即计算向量并将其粘贴在过滤器表达式中。它不能再被列屏蔽。内联显示为 qq_show()
:
MyFilter <- function(data, filtersVector) {
rlang::qq_show(
data %>% filter(Species %in% !!filtersVector)
)
}
MyFilter(iris2, c("setosa", "virginica"))
#> data %>% filter(Species %in% <chr: "setosa", "virginica">)