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