使用传递给dplyr :: filter的参数创建一个函数,解决nse的最佳方法是什么?

Pau*_*eux 9 r dplyr nse

使用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)

3个实施例

要解决非标准评估问题,我通常会使用下划线附加参数:

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函数,实现这个过滤函数的首选方法是什么?还有其他方法吗?

edd*_*ddi 5

这个问题与非标准评估完全无关。让我重写您的初始函数以使内容更清楚:

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)


jai*_*ash 5

@eddi的答案对于这里发生的事情是正确的.我正在写另一个答案,解决了如何使用dplyr动词编写函数的更大请求.你会注意到,它最终会使用类似的东西nrowspecies2来避免species == species重言式.

编写一个包含将与NSE一起使用的dplyr动词的函数,请编写两个函数:

首先编写一个需要引用输入的版本,使用lazyevaldplyr动词的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参数