我有一个带有参数subset
的函数,其默认值为NULL
。在函数中,如果subset
是NULL
我不想添加条件管道。否则,我想在管道中使用subset
的值:
library(tidyverse)
f <- function(subset = NULL){
iris %>%
{if (is.null(substitute(subset))) . else filter(., {{ subset }} < 2.2)}
}
f() # gives error posted below
## Desired output: entire iris dataset
f(subset = Sepal.Width) # works
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5 2 3.5 1 versicolor
但是,使用大括号,{{ subset}}
在subset = NULL
时评估得太早,并试图过滤NULL < 2.2
的位置。f()
返回以下错误:
错误:输入
..1
filter()
问题。x 输入
..1
的大小必须为 150 或 1,而不是大小为 0。i 输入
..1
是NULL < 2.2
。
您应该在函数体中计算is.null(substitute(subset))
,而不是在if
子句中计算。该子句的计算方式(由于堆栈管理%>%
)与父子句的计算方式不同。
这有效:
f <- function(subset = NULL){
isnull <- is.null(substitute(subset))
iris %>%
{if (isnull) . else filter(., {{ subset }} < 2.2)}
}
head( f() )
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
# 4 4.6 3.1 1.5 0.2 setosa
# 5 5.0 3.6 1.4 0.2 setosa
# 6 5.4 3.9 1.7 0.4 setosa
f(subset = Sepal.Width)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5 2 3.5 1 versicolor
这是一种在发生error
时返回完整数据集的方法tryCatch
f <- function(subset = NULL){
tryCatch( iris %>%
filter({{ subset }} < 2.2)
, error = function(err) iris)
}
-测试
dim(f())
#[1] 150 5
dim(f(subset = Sepal.Width))
#[1] 1 5
我发现了一种可能不是最优雅的方法,但它的优点是相当清晰和直观。
首先,我将对象转换为字符串,并与数据集中的变量进行比较。如果您输入的对象不存在,它将按原样返回数据。如果该列依次在数据集中,它将使用 bang-bang 运算符对其进行过滤,!!
结合使用sym
将您的字符串转换为符号。
希望这对你有用。
library(dplyr)
f <- function(data, subset = NULL, value = 2.2) {
subsetName = deparse(substitute(subset))
if (!subsetName %in% names(data)) {return(data)}
return(data %>% filter(!!sym(subsetName) < value))
}