Reputation: 18612
I have a function with an argument subset
that has the default value of NULL
. Within the function if subset
is NULL
I do not want to add a conditional pipe. Otherwise, I want to use the value of subset
within the pipe:
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
However, using the curly brackets, {{ subset}}
is evaluating too early when subset = NULL
and is trying to filter where NULL < 2.2
. f()
returns the following error:
Error: Problem with
filter()
input..1
.x Input
..1
must be of size 150 or 1, not size 0.i Input
..1
isNULL < 2.2
.
Upvotes: 2
Views: 579
Reputation: 886938
Here is an approach to return the full dataset when an error
happens with tryCatch
f <- function(subset = NULL){
tryCatch( iris %>%
filter({{ subset }} < 2.2)
, error = function(err) iris)
}
-testing
dim(f())
#[1] 150 5
dim(f(subset = Sepal.Width))
#[1] 1 5
Upvotes: 2
Reputation: 160407
You should evaluate is.null(substitute(subset))
in the function body, not within the if
clause. That clause is evaluated differently (due to %>%
stack management) than in the parent.
This works:
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
Upvotes: 2
Reputation: 683
I found an approach which is probably not the most elegant, but it has the advantage of being fairly legible and intuitive.
First I turn the object into a string and compare with the variables in the dataset. If the object you entered is not there, it will just return the data as is. If in turn the column is in the dataset, it will filter it using the bang-bang operator !!
in combination with sym
which converts your string to a symbol.
Hope that works for you.
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))
}
Upvotes: 1