I often use lists of lists to apply a function (often a model call) onto a grid of parameters.
Here is an example with paste
as the ultimate function:
library(tidyverse) #purrr
a=c("A", "B", "C") %>% set_names %>% map(function(x){
c("m0", "m1") %>% set_names %>% map(function(y){
c("absolute", "relative") %>% set_names %>% map(function(z){
paste(x,y,z)
})
})
})
a$A$m0$absolute #exact expected output
I am looking for a way to get the exact same result with a simpler call, probably by using cross
or expand.grid
and pmap
or at_depth
.
I got something interesting with pmap
+ expand.grid
but it flattened the structure and droped the names:
b=expand.grid(variable=c("A", "B", "C"), model=c("m0", "m1"), type=c("absolute", "relative"))
a=b %>% pmap(~{paste(..1,..2,..3)}) #a simple list of length 12
In the best case, the function would even be able to use names (variable, model, type
) inside the map call (instead of ..1,..2,..3
for pmap
).
Is there a way to get this?
As I understand the question, you need to evaluate a function over a combination of parameters and save the result in a nested list structure defined by the combination of parameters hierarchically. Using a function with recursion, an approach is:
ff = function(args, FUN, init = list(), collect = list())
{
if(!length(args)) return(as.call(c(FUN, collect)))
for(i in 1:length(args[[1]]))
init[[args[[1]][i]]] = ff(args[-1], FUN = FUN,
collect = c(collect, setNames(list(args[[1]][i]), names(args)[1])))
return(init)
}
The above function returns an unevaluated call (for the sake of the example; in actual usage do.call(FUN, collect)
should replace as.call(c(FUN, collect))
) for each combination of parameters. E.g.:
ff(list(param1 = c("a", "b"), param2 = c("A", "B")), c)
#$a
#$a$A
#.Primitive("c")(param1 = "a", param2 = "A")
#
#$a$B
#.Primitive("c")(param1 = "a", param2 = "B")
#
#
#$b
#$b$A
#.Primitive("c")(param1 = "b", param2 = "A")
#
#$b$B
#.Primitive("c")(param1 = "b", param2 = "B")
Comparing against the example:
b = ff(list(variable = c("A", "B", "C"),
model = c("m0", "m1"),
type = c("absolute", "relative")),
paste)
b #to see the object
identical(a, rapply(b, eval, how = "replace"))
#[1] TRUE
b$A$m0$absolute
#(function (..., sep = " ", collapse = NULL)
#.Internal(paste(list(...), sep, collapse)))(variable = "A", model = "m0",
# type = "absolute")
eval(b$A$m0$absolute)
#[1] "A m0 absolute"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With