首页 文章

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

提问于
浏览
8

使用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 在iris数据框的上下文中进行解释,而不是在函数参数的上下文中进行解释:

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 answer解决非标准评估的方法 . interp() 在函数环境的上下文中解释 species

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

考虑到上面的3函数,实现这个过滤函数的首选方法是什么?还有其他方法吗?

3 回答

  • 0

    @eddi 的答案是正确的's going on here. I'正在编写另一个答案,解决了如何使用 dplyr 动词编写函数的更大请求 . 你会注意到,最终,它使用像 nrowspecies2 这样的东西来避免 species == species 重言式 .

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

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

    Second 制作使用NSE的版本:

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

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

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

  • 5

    这个问题与非标准评估完全无关 . 让我重写你的初始函数,使其清楚:

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

    filter 中的表达式总是求值为 TRUE (几乎总是 - 见下面的例子),这是因为某些NSE魔法,所以_1872203不起作用 .

    你的 nrowspecies2 是要走的路 .

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

  • 3

    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
    

相关问题