Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

apply a function to a parameter grid and return a list of lists in purrr

Tags:

r

purrr

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?

like image 650
Dan Chaltiel Avatar asked Nov 06 '22 11:11

Dan Chaltiel


1 Answers

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"
like image 177
alexis_laz Avatar answered Nov 14 '22 21:11

alexis_laz