使用dplyr的动词时,非标准评估非常方便.但是当使用带有函数参数的动词时,它可能会有问题.例如,让我们说我想创建一个函数,它给出了给定物种的行数.
# Load packages and prepare data
library(dplyr)
library(lazyeval)
# I prefer lowercase column names
names(iris) <- tolower(names(iris))
# Number of rows for all species
nrow(iris)
# [1] 150
Run Code Online (Sandbox Code Playgroud)
此函数无法按预期工作,因为species
它在iris数据框的上下文中进行解释,而不是在函数参数的上下文中进行解释:
nrowspecies0 <- function(dtf, species){
dtf %>%
filter(species == species) %>%
nrow()
}
nrowspecies0(iris, species = "versicolor")
# [1] 150
Run Code Online (Sandbox Code Playgroud)
要解决非标准评估问题,我通常会使用下划线附加参数:
nrowspecies1 <- function(dtf, species_){
dtf %>%
filter(species == species_) %>%
nrow()
}
nrowspecies1(iris, species_ = "versicolor")
# [1] 50
# Because of function name completion the argument
# species works too
nrowspecies1(iris, species = "versicolor")
# [1] 50
Run Code Online (Sandbox Code Playgroud)
它并不完全令人满意,因为它将函数参数的名称更改为不太用户友好的名称.或者它依赖于自动完成,我担心这不是编程的好习惯.为了保持一个好的参数名称,我可以这样做:
nrowspecies2 <- function(dtf, species){
species_ <- species
dtf %>%
filter(species == species_) %>%
nrow()
}
nrowspecies2(iris, species = "versicolor")
# [1] 50
Run Code Online (Sandbox Code Playgroud)
另一种基于此答案解决非标准评估的方法.
在函数环境的上下文中interp()解释species:
nrowspecies3 <- function(dtf, species){
dtf %>%
filter_(interp(~species == with_species,
with_species = species)) %>%
nrow()
}
nrowspecies3(iris, species = "versicolor")
# [1] 50
Run Code Online (Sandbox Code Playgroud)
考虑到上面的3函数,实现这个过滤函数的首选方法是什么?还有其他方法吗?
这个问题与非标准评估完全无关。让我重写您的初始函数以使内容更清楚:
nrowspecies4 <- function(dtf, boo){
dtf %>%
filter(boo == boo) %>%
nrow()
}
nrowspecies4(iris, boo = "versicolor")
#150
Run Code Online (Sandbox Code Playgroud)
您内部的表达式filter始终计算为TRUE(几乎总是-参见下面的示例),这就是为什么它不起作用的原因,不是因为有些NSE魔术。
你nrowspecies2是要走的路。
Fwiw,species在您nrowspecies0中的确是作为列而不是输入变量来求值的species,您可以通过与进行比较nrowspecies0(iris, NA)来进行检查nrowspecies4(iris, NA)。
@eddi的答案对于这里发生的事情是正确的.我正在写另一个答案,解决了如何使用dplyr动词编写函数的更大请求.你会注意到,它最终会使用类似的东西nrowspecies2来避免species == species重言式.
要编写一个包含将与NSE一起使用的dplyr动词的函数,请编写两个函数:
首先编写一个需要引用输入的版本,使用lazyeval和dplyr动词的SE版本.所以在这种情况下,filter_.
nrowspecies_robust_ <- function(data, species){
species_ <- lazyeval::as.lazy(species)
condition <- ~ species == species_ # *
tmp <- dplyr::filter_(data, condition) # **
nrow(tmp)
}
nrowspecies_robust_(iris, ~versicolor)
Run Code Online (Sandbox Code Playgroud)
第二个是使用NSE的版本:
nrowspecies_robust <- function(data, species) {
species <- lazyeval::lazy(species)
nrowspecies_robust_(data, species)
}
nrowspecies_robust(iris, versicolor)
Run Code Online (Sandbox Code Playgroud)
* =如果你想做一些更复杂的事情,你可能需要在lazyeval::interp这里使用,如下面链接的提示
** =另外,如果需要更改输出名称,请参阅.dots参数
对于上述内容,我遵循了哈德利的一些提示
另一种很好的资源是在NSE的dplyr晕影,其示出.dots,interp以及从其它功能lazyeval包
关于lazyeval的更多细节,请看它的插图
有关使用NSE的基本R工具的详尽讨论(其中许多可lazyeval帮助您避免),请参阅高级R中的NSE章节