Home > Software engineering >  How to pass multiple different arguments to a set of parameters in R functions?
How to pass multiple different arguments to a set of parameters in R functions?

Time:03-24

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

  • Related