I want to create a function that can pass multiple different arguments to sets of parameters in R user-defined functions.
I am using dplyr to create functions that can work with tidyverse ecosystem.
For example:
library(dplyr)
# Create the function
myfunction <- function(.data, ..., ...) {
.action_vars <- enquos(...)
.group_vars <- enquos(...)
.data %>%
group_by(!!!.group_vars) %>%
other_function(!!!.action_vars, parameter_x = "other_argument")
}
# Apply the function
result <- myfunction(MyData, Var1, Var2, Var3, Var4)
Following the example let's say I want .action_vars = Var1 and Var2 and .group_vars = Var3 and Var4
I know I cannot use the three-dot ellipsis twice in my defined function. I'd love to hear how you would solve this problem. I have looked everywhere but I seem to not find the answer.
CodePudding user response:
I would do this using arrays containing text strings:
# Create the function
myfunction <- function(.data, action_vars, group_vars) {
.action_vars <- action_vars %>% enquos(...)
.group_vars <- group_vars %>% enquos(...)
.data %>%
group_by(!!!.group_vars) %>%
other_function(!!!.action_vars, parameter_x = "other_argument")
}
Then you can provide an array of text strings using the standard c()
as so:
# Apply the function
result <- myfunction(MyData, c("Var1", "Var2"), c("Var3", "Var4"))
You may need to use sym
or syms
instead of enquo
or enquos
because your inputs are now text strings.
CodePudding user response:
Use across()
as a selection bridge to take a selection of multiple variables in a single argument:
my_function <- function(data, group_vars, action_vars) {
data |>
# Use `across()` as a selection bridge within `group_by()`
group_by(across({{ group_vars }})) |>
# Pass selection directly to `select()`
select({{ action_vars }})
}
mtcars |>
my_function(c(cyl, am), disp:drat)
#> Adding missing grouping variables: `cyl`, `am`
#> # A tibble: 32 × 5
#> # Groups: cyl, am [6]
#> cyl am disp hp drat
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 6 1 160 110 3.9
#> 2 6 1 160 110 3.9
#> 3 4 1 108 93 3.85
#> 4 6 0 258 110 3.08
#> # … with 28 more rows
across()
is also convenient for complex operations since you can pass a function to map over the selection:
my_function <- function(data, group_vars, action_vars) {
data |>
group_by(across({{ group_vars }})) |>
summarise(across({{ action_vars }}, \(x) mean(x, na.rm = TRUE)))
}
mtcars |>
my_function(c(cyl, am), disp:drat)
#> `summarise()` has grouped output by 'cyl'. You can override using the `.groups`
#> argument.
#> # A tibble: 6 × 5
#> # Groups: cyl [3]
#> cyl am disp hp drat
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 4 0 136. 84.7 3.77
#> 2 4 1 93.6 81.9 4.18
#> 3 6 0 205. 115. 3.42
#> 4 6 1 155 132. 3.81
#> 5 8 0 358. 194. 3.12
#> 6 8 1 326 300. 3.88
Learn more about this pattern in https://rlang.r-lib.org/reference/topic-data-mask-programming.html#bridge-patterns