Home > Net >  How to match many combinations of function arguments in R?
How to match many combinations of function arguments in R?

Time:10-25

Can we create a 2-dimensional array for matching combinations of two function arguments?

For example, if I write a function with 1 argument (in addition to data input argument):

choose_procedure <- function(x, what_to_do) {
  switch(what_to_do,
         "mean"   = {...}, # mean(x) or mean(x, na.rm = TRUE) or weighted.mean(x)
         "median" = {...}, # median(x)
         "square" = {...}, # x * x or x ^ 2
         "unique" = {...}, # unique(x)
         "log"    = {...}  # log(x) or log10(x)
  )
}

I added inline comments to imply that there could be more than one choice per what_to_do input.

When what_to_do = "mean", should it be mean(x, na.rm = TRUE) or mean(x, na.rm = FALSE)? Likewise, when what_to_do = "log", should it be log(x) or log10(x)? Etc.

To handle this, I thought to introduce another argument to choose_procedure(), called "scenario". So if the call to choose_procedure() is:

choose_procedure(x = mtcars$mpg, what_to_do = "log", scenario = "A")

Then it would execute log(mtcars$mpg).
But if the call is

choose_procedure(x = mtcars$mpg, what_to_do = "log", scenario = "B")

then it would execute log10(mtcars$mpg).


An example with just "log" and "scenario" describes a 2x2 array:

  • "what_to_do" has 2 options: log() or log10()
  • "scenario" has two options: "A" or "B"

Clearly, this could be handled with 4 if-statements (one for each combination), but will become very difficult to program if we have more combinations (as in choose_procedure() example I opened with).

So I have two questions:

  1. I'm looking for a setup that could be extended to potentially any n × n array.
  2. In fact, maybe there's a way to generalize to more than n × n? For example, if we have 3 arguments: "what_do_to", "scenario", "sub_scenario". Etc.

CodePudding user response:

choose_procedure <- function(x, FUN, ...){
   if(...length()) FUN(x, ...)
   else FUN(x)
 }

x <- c(1,3,5,NA, 10)

choose_procedure(x, mean)
[1] NA

choose_procedure(x, mean, na.rm = TRUE)
[1] 4.75

choose_procedure(x, log)
[1] 0.000000 1.098612 1.609438       NA 2.302585

choose_procedure(x, log10)
[1] 0.0000000 0.4771213 0.6989700        NA 1.0000000

CodePudding user response:

This is one of those things where there are many ways to tackle the problem, and the best one is very likely to be dependant on the context you actually want to use it in. However, in the situation you outlined, here's how I would approach it:

choose_procedure <- function(x, ...) {
  
  # Define a table of options
  choices <- tibble::tribble(
    ~what_to_do,  ~scenario,                ~result,
         "mean",        "A",                   mean,
         "mean",        "B", ~mean(., na.rm = TRUE),
         "mean",        "C",          weighted.mean,
       "median",        "A",                 median,
       "square",        "A",                 ~. * .,
       "square",        "B",                 ~. ^ 2,
       "unique",        "A",                 unique,
          "log",        "A",                    log,
          "log",        "B",                  log10
  )
  
  # Filter the table down to the desired option
  choice <- dplyr::filter(choices, ...)
  
  # Stop if no options available
  if (nrow(choice) == 0) {
    stop("No such option available")
  }
  
  # Warn if multiple options available, and use first
  if (nrow(choice) > 1) {
    choice <- head(choices, 1)
    warning("More than one option available, using first scenario")
  }
  
  # Transform any purrr-style lambda functions to normal functions
  fun <- rlang::as_function(choice$result[[1]])
  
  # Perform the calculation
  fun(x)
  
}

choose_procedure(x = mtcars$mpg, what_to_do == "log", scenario == "B")
#>  [1] 1.322219 1.322219 1.357935 1.330414 1.271842 1.257679 1.155336 1.387390
#>  [9] 1.357935 1.283301 1.250420 1.214844 1.238046 1.181844 1.017033 1.017033
#> [17] 1.167317 1.510545 1.482874 1.530200 1.332438 1.190332 1.181844 1.123852
#> [25] 1.283301 1.436163 1.414973 1.482874 1.198657 1.294466 1.176091 1.330414

Created on 2021-10-25 by the reprex package (v2.0.0)

  • Related