使用传递给 dplyr::filter 的参数创建函数什么是解决 nse 的最佳方法?
Creating a function with an argument passed to dplyr::filter what is the best way to work around 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
在 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个函数,
实现此过滤功能的首选(最可靠)方法是什么?
还有其他方法吗?
本题与非标准评价完全无关。让我重写您的初始函数以明确说明:
nrowspecies4 <- function(dtf, boo){
dtf %>%
filter(boo == boo) %>%
nrow()
}
nrowspecies4(iris, boo = "versicolor")
#150
您的 filter
中的表达式总是求值为 TRUE
(几乎总是 - 请参见下面的示例),这就是它不起作用的原因,而不是因为某些 NSE 魔术。
您的 nrowspecies2
是正确的选择。
Fwiw,nrowspecies0
中的 species
确实被评估为列,而不是输入变量 species
,您可以通过比较 nrowspecies0(iris, NA)
来检查nrowspecies4(iris, NA)
.
@eddi 的回答关于这里发生的事情是正确的。
我正在写另一个答案,以解决关于如何使用 dplyr
动词编写函数的更大请求。你会注意到,最终,它使用类似 nrowspecies2
的东西来避免 species == species
重言式。
要写一个函数包装 dplyr 动词,它将与 NSE 一起工作,写两个函数:
首先 编写一个需要引用输入的版本,使用 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
参数
以上,我按照some tips from Hadley
另一个很好的资源是 the dplyr vignette on NSE,它说明了 .dots
、interp
和 lazyeval
包
中的其他函数
有关lazyeval的更多详细信息see it's vignette
有关使用 NSE 的基本 R 工具的详尽讨论(其中许多 lazyeval
可帮助您避免),请参阅 the chapter on NSE 中的 Advanced R
in his 2016 UseR talk (@38min30s), Hadley Wickham explains the concept of referential transparency。使用公式,过滤函数可以重新表述为:
nrowspecies5 <- function(dtf, formula){
dtf %>%
filter_(formula) %>%
nrow()
}
这具有更通用的额外好处
# Make column names lower case
names(iris) = tolower(names(iris))
nrowspecies5(iris, ~ species == "versicolor")
# 50
nrowspecies5(iris, ~ sepal.length > 6 & species == "virginica")
# 41
nrowspecies5(iris, ~ sepal.length > 6 & species == "setosa")
# 0
非标准评价真的好用 使用 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个函数, 实现此过滤功能的首选(最可靠)方法是什么? 还有其他方法吗?
本题与非标准评价完全无关。让我重写您的初始函数以明确说明:
nrowspecies4 <- function(dtf, boo){
dtf %>%
filter(boo == boo) %>%
nrow()
}
nrowspecies4(iris, boo = "versicolor")
#150
您的 filter
中的表达式总是求值为 TRUE
(几乎总是 - 请参见下面的示例),这就是它不起作用的原因,而不是因为某些 NSE 魔术。
您的 nrowspecies2
是正确的选择。
Fwiw,nrowspecies0
中的 species
确实被评估为列,而不是输入变量 species
,您可以通过比较 nrowspecies0(iris, NA)
来检查nrowspecies4(iris, NA)
.
@eddi 的回答关于这里发生的事情是正确的。
我正在写另一个答案,以解决关于如何使用 dplyr
动词编写函数的更大请求。你会注意到,最终,它使用类似 nrowspecies2
的东西来避免 species == species
重言式。
要写一个函数包装 dplyr 动词,它将与 NSE 一起工作,写两个函数:
首先 编写一个需要引用输入的版本,使用 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
参数
以上,我按照some tips from Hadley
另一个很好的资源是 the dplyr vignette on NSE,它说明了
中的其他函数.dots
、interp
和lazyeval
包有关lazyeval的更多详细信息see it's vignette
有关使用 NSE 的基本 R 工具的详尽讨论(其中许多
lazyeval
可帮助您避免),请参阅 the chapter on NSE 中的 Advanced R
in his 2016 UseR talk (@38min30s), Hadley Wickham explains the concept of referential transparency。使用公式,过滤函数可以重新表述为:
nrowspecies5 <- function(dtf, formula){
dtf %>%
filter_(formula) %>%
nrow()
}
这具有更通用的额外好处
# Make column names lower case
names(iris) = tolower(names(iris))
nrowspecies5(iris, ~ species == "versicolor")
# 50
nrowspecies5(iris, ~ sepal.length > 6 & species == "virginica")
# 41
nrowspecies5(iris, ~ sepal.length > 6 & species == "setosa")
# 0