rparameter-passingmicrobenchmark

R: Using a variable to to pass multiple values for a single dynamically-defined parameter into a function


I would like to be able to pass a parameter through two functions, but defining the parameter that is use with a variable. This would avoid having to specify every possibility of what the parameter could be.

See the following example for what I'd like to achieve:

run_multiple <- function (parameter_name, parameter_values) {
  for (value in parameter_values) {
    run_function(parameter_name = value)
  }
}

run_function <- function (x = "a", y = "a", z = "a") {
  print(sprintf("X is %s, Y is %s, Z is %s", x, y, z))
}

Then implementation would ideally work using either of these formats:

run_multiple("x", c("a", "b"))
run_multiple(x, c("a", "b"))
# [1] X is a, Y is a, Z is a
# [1] X is b, Y is a, Z is a

The above would call run_function(x = "a") and run_function(x = "b") within the loop of run_multiple.

I don't think ... notation in run_multiple would help as far as I can tell, as it would only allow me to pass through the single value, not multiple values in the loop.

If it's useful, the wider context of this is to be used alongside microbenchmarking for a large function, to see how enabling different versions of sub-functions affects the overall performance.

Example of how this works:

run_benchmarking <- function (parameter_name, parameter_values, run_names) {
  # currently setup with only two values allowed
  microbenchmark::microbenchmark(
      run_names[1] = run_function(parameter_name = parameter_values[1]),
      run_names[2] = run_function(parameter_name = parameter_values[2]),
    times = 1000
  )
}

run_function <- function (use_x = T, use_y = T, use_z = T) {
  x <- func1(use_x)
  y <- func2(use_y)
  z <- func3(use_z)
  
  list(x = x, y = y, z = z)
}

# example of func n
func1 <- function (use_x) {
  if (use_x) {
    x <- round(runif(1, 1, 100))
  } else {
    x <- 1
  }
}

# example of benchmarking tests, which would output results from microbenchmark
run_benchmarking(use_x, c(T, F), c("With X", "Without X"))
run_benchmarking(use_y, c(T, F), c("With Y", "Without Y"))
run_benchmarking(use_z, c(T, F), c("With Z", "Without Z"))

This currently only accepts two values into a parameter which is something that could be expanded, but should be sufficient for A/B testing initially.

I'm also open to other suggestions if there's a better way to test over a number of A/B tests like this.


Solution

  • run_multiple <- function (parameter_name, parameter_values) {
      parameter_name <- as.character(substitute(parameter_name))
      for (value in parameter_values) {
        arg <- setNames(list(value), parameter_name)
        do.call(run_function, arg)
      }
    }
    
    run_function <- function (x = "a", y = "a", z = "a") {
      print(sprintf("X is %s, Y is %s, Z is %s", x, y, z))
    }
    
    run_multiple("x", c("a", "b"))
    #[1] "X is a, Y is a, Z is a"
    #[1] "X is b, Y is a, Z is a"
    run_multiple(x, c("a", "b"))
    # [1] X is a, Y is a, Z is a
    # [1] X is b, Y is a, Z is a
    

    This can be extended to multiple parameter names. Of course that would be easier without non-standard evaluation because then you could just use a vector of parameter names and a list or matrix of corresponding value vectors as input.