I have the following function:
my_fct <-
function(input1, input2, input3) {
output <- df %>%
filter(x = input1 | x = input2 | x = input3
)
}
with let's say input1 = c(1:10), input2 = c(1:10), input3 = c(1:10)
In some occassions, I only need one or two of the three inputs, like
output <- my_fct(3, 5)
Unfortunately, my approach requires to insert exactly three arguments. Is there a way to create a function, in which the argument input can vary?
I really appreciate your help, thanks
I'm going to demonstrate using this dataset:
DAT <- head(mtcars)
names(DAT)[2] <- "x"
Do you mean filter(x == input1 | ...)
? x=input1
is unlikely to work as I'm inferring.
Functions should not be breaking scope to assume that an object is visible in the calling environment (df
here). Doing it this way breaks reproducibility and can make troubleshooting more difficult. In this case, it can be more difficult since if df
does not exist, the error will likely reference function
or closure
and not just say object 'df
' not found`, which can be confusing if you aren't familiar with what's going on.
For the examples below, I'll explicitly add .data
as the first argument to work around this. (Making it the first argument means it'll play nicely with dplyr.) Similarly, I'll guard against "x"
not being found in the data ... it might be useful to read https://dplyr.tidyverse.org/articles/programming.html for this, though a bit more work and out of scope for this answer.
We can use default values of NULL
, perhaps
my_fct <- function(.data, input1 = NULL, input2 = NULL, input3 = NULL) {
stopifnot("x" %in% names(.data))
.data %>%
filter(
if (is.null(input1)) FALSE else x == input1 |
if (is.null(input2)) FALSE else x == input2 |
if (is.null(input3)) FALSE else x == input3
)
}
my_fct(DAT)
# [1] mpg x disp hp drat wt qsec vs am gear carb
# <0 rows> (or 0-length row.names)
my_fct(DAT, 4)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
my_fct(DAT, 4, 8)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
# Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2
(We can't use is.null(input1) | x == input1
since both sides of |
are always run, and x == NULL
returns a 0-length vector instead of what we need.)
If input*
are always going to be length-1 vectors, we can simplify it a little with
my_fct <- function(.data, input1 = NULL, input2 = NULL, input3 = NULL) {
stopifnot("x" %in% names(.data))
.data %>%
filter(x %in% c(input1, input2, input3))
}
my_fct(DAT)
# [1] mpg x disp hp drat wt qsec vs am gear carb
# <0 rows> (or 0-length row.names)
my_fct(DAT, 4)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
my_fct(DAT, 4, 8)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
# Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2
If you are looking for something a bit more flexible (such as someday wanting input4
as well, then perhaps
my_fct <- function(.data, ...) {
stopifnot("x" %in% names(.data))
dots <- list(...)
if (!length(dots)) return(.data[0,])
.data[rowSums(sapply(dots, function(z) .data$x == z)) > 0,]
}
my_fct(DAT)
# [1] mpg x disp hp drat wt qsec vs am gear carb
# <0 rows> (or 0-length row.names)
my_fct(DAT, 4)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
my_fct(DAT, 4, 8)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
# Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2
my_fct(DAT, 4, 11, 29, 394, 8)
# mpg x disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
# Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2