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

时间:2016-04-15 12:42:34

标签: 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

示例不起作用

此功能无法正常工作,因为species 在虹膜数据帧的上下文中进行解释 而不是在上下文中解释 函数参数:

nrowspecies0 <- function(dtf, species){
    dtf %>%
        filter(species == species) %>%
        nrow()
}
nrowspecies0(iris, species = "versicolor")
# [1] 150

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

这并不完全令人满意 因为它将函数参数的名称更改为 不太方便用户的东西。或者它依赖于自动完成 我担心这不是编程的好习惯。 为了保持一个好的参数名称, 我能做到:

nrowspecies2 <- function(dtf, species){
    species_ <- species
    dtf %>%
        filter(species == species_) %>%
        nrow()
}
nrowspecies2(iris, species = "versicolor")
# [1] 50

解决非标准评估的另一种方法 基于this answerinterp()在语境中解释species 功能环境:

nrowspecies3 <- function(dtf, species){
    dtf %>%
        filter_(interp(~species == with_species, 
                       with_species = species)) %>%
        nrow()
}
nrowspecies3(iris, species = "versicolor")
# [1] 50

考虑到上面的3个功能, 什么是首选 - 最强大 - 实现此过滤功能的方法? 还有其他方法吗?

3 个答案:

答案 0 :(得分:5)

@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) 

第二次制作使用NSE的版本:

nrowspecies_robust <- function(data, species) { 
  species <- lazyeval::lazy(species) 
  nrowspecies_robust_(data, species) 
} 
nrowspecies_robust(iris, versicolor) 

* =如果你想做一些更复杂的事情,你可能需要在这里使用lazyeval::interp,如下面提到的提示

** =如果您需要更改输出名称,请参阅.dots参数

答案 1 :(得分:3)

这个问题与非标准评估完全无关。让我重写你的初始功能,以明确说明:

nrowspecies4 <- function(dtf, boo){
    dtf %>%
        filter(boo == boo) %>%
        nrow()
}
nrowspecies4(iris, boo = "versicolor")
#150

filter内的表达式始终评估为TRUE(几乎总是 - 请参阅下面的示例),这就是为什么它不起作用,而不是因为某些NSE魔法。

您的nrowspecies2是可行的方式。

Fwiw,您species中的nrowspecies0确实被评估为列,而不是输入变量species,您可以通过将nrowspecies0(iris, NA)与{{进行比较来检查1}}。

答案 2 :(得分:0)

in his 2016 UseR talk(@ 38min30s),Hadley Wickham解释了referential transparency的概念。使用公式,过滤函数可以重新表述为:

nrowspecies5 <- function(dtf, formula){
    dtf %>%
        filter_(formula) %>%
        nrow()
}

这具有更多通用的附加好处

nrowspecies5(iris, ~ species == "versicolor")
# 50
nrowspecies5(iris, ~ sepal.length > 6 & species == "virginica")
# 41
nrowspecies5(iris, ~ sepal.length > 6 & species == "setosa")
# 0