dea*_*ann 1 r tidyverse tidyeval
我偶然发现了这种行为,并不太了解。有人可以请问一下吗?
我写了下面的函数,它给出以下错误:
> 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.
Run Code Online (Sandbox Code Playgroud)
但是,如果我以以下方式对其进行修改,则它将按预期工作:
> 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
Run Code Online (Sandbox Code Playgroud)
我也意识到在一个函数中我应该使用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
Run Code Online (Sandbox Code Playgroud)
但是,我仍然对上述行为感到困惑,不胜感激。
TLDR:在第一个版本中,您创建了一个自引用(一个指向自身的符号)。其他版本也可以使用,但是实际上您不需要在这里使用quasure和捕获参数,因为您没有在引用数据框列。这也解释了为什么quo()
和enquo()
版本都相同的原因。您可以按常规方式传递参数,而无需使用任何引号,尽管取消引用还是个好主意,!!
以避免任何数据掩盖错误。
您可以qq_show()
在filter()
调用周围使用,以发现语法差异:
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))
Run Code Online (Sandbox Code Playgroud)
因此,我们在这里要求filter()
查找Species
与元素匹配的行filtersVector
。filtersVector
数据框中没有任何列,因此它在quosure环境中查找定义。您创建了一个带有quosure quo()
,记录你的表达(在这种情况下,一个符号filtersVector
)和你的 envionment(你的函数的环境)。因此它查找一个filtersVector
对象,其中包含一个指向自身的符号。它仅被评估一次,因此没有无限循环,但是您实际上正在尝试将向量与符号进行比较,这是类型错误:
"setosa" %in% quote(filtersVector)
#> Error in match(x, table, nomatch = 0L) :
#> 'match' requires vector arguments
Run Code Online (Sandbox Code Playgroud)
在第二次尝试中,您给quasure取了另一个名字。它之所以起作用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")))
Run Code Online (Sandbox Code Playgroud)
现在,Quasure包含一个调用,该调用可以在现场创建矢量,这很%in%
容易理解。
请注意,尽管如此,您实际上并未真正引用数据框列。您正在传递矢量。这意味着您根本不需要任何保证,也不需要捕获传递给参数的表达式。enquo()
仅可用于将评估延迟到最后,因此可以在数据帧内进行评估。如果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
Run Code Online (Sandbox Code Playgroud)
有用!但是,如果数据框包含一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>
Run Code Online (Sandbox Code Playgroud)
因此,取消引用仍然是一个好主意,因为这将立即评估向量并将其粘贴在过滤器表达式中。它不能再被列掩盖。内联显示为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">)
Run Code Online (Sandbox Code Playgroud)