I would like to loop through a function trying different combinations of parameters. I would like to specify the parameters in a list (i.e. a structured format)
In the following examples, I would like the outputs to be:
# n=0, m=0: 1, 2, 3, 4
# n=1, m=0: 2, 3, 4, 5
# n=1, m=10: 20, 30, 40, 50
test <- function(x, n = 0, m = 1) {(x + n) * m}
vals <- list(1, 2, 3, 4)
arg_list <- list(args1 = list(),
args1 = list(n = 1),
args2 = list(n = 1, m = 10))
lapply(vals, test, arg_list[[1]])
lapply(vals, test, arg_list[[2]])
lapply(vals, test, arg_list[[3]])
But this produces an error.
I can get the functionality I am seeking by doing the following, but this does not seem to be the most elegant implementation.
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[1]])))
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[2]])))
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[3]])))
Essentially I am asking how to pass in ellipsis arguments as one object when used with lapply().
lapply(X, FUN, ...)
Any help greatly appreciated.
You just need one more layer:
lapply(arg_list, \(arg)
lapply(vals, \(val)
do.call(test, c(x = val, arg))
)
)
# $args1
# $args1[[1]]
# [1] 1
#
# $args1[[2]]
# [1] 2
#
# $args1[[3]]
# [1] 3
#
# $args1[[4]]
# [1] 4
#
#
# $args1
# $args1[[1]]
# [1] 2
#
# $args1[[2]]
# [1] 3
#
# $args1[[3]]
# [1] 4
#
# $args1[[4]]
# [1] 5
#
#
# $args2
# $args2[[1]]
# [1] 20
#
# $args2[[2]]
# [1] 30
#
# $args2[[3]]
# [1] 40
#
# $args2[[4]]
# [1] 50
To demonstrate equivalency:
## from the question
op = list(
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[1]]))),
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[2]]))),
lapply(vals, FUN = function(x) do.call(test, c(x = x, arg_list[[3]])))
)
## from the answer
gregor = lapply(arg_list, \(arg)
lapply(vals, \(x)
do.call(test, c(x = x, arg))
)
)
identical(op, unname(gregor))
# [1] TRUE
You could also do some automatic simplification by making the inner call an sapply
instead of lapply
:
lapply(arg_list, \(arg)
sapply(vals,
\(x) do.call(test, c(x = x, arg))
)
)
$args1
[1] 1 2 3 4
$args1
[1] 2 3 4 5
$args2
[1] 20 30 40 50
(Note the repeated names in the output match the repeated names in the input.)