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

别来无恙 提交于 2020-01-13 02:53:17

问题


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?


回答1:


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"



回答2:


This seems to be what you want

variable <- c("A", "B", "C")
model <- c("m0", "m1")
type <- c("absolute", "relative")
cross3(variable, model, type) %>% 
    setNames(., map(., paste, collapse="_")) %>%
    map(paste, collapse=" ")

Which gives

#> $A_m0_absolute
#> [1] "A m0 absolute"
#> 
#> $B_m0_absolute
#> [1] "B m0 absolute"
#> 
#> $C_m0_absolute
#> [1] "C m0 absolute"
#> 
#> $A_m1_absolute
#> [1] "A m1 absolute"
#> 
#> $B_m1_absolute
#> [1] "B m1 absolute"
#> 
#> $C_m1_absolute
#> [1] "C m1 absolute"
#> 
#> $A_m0_relative
#> [1] "A m0 relative"
#> 
#> $B_m0_relative
#> [1] "B m0 relative"
#> 
#> $C_m0_relative
#> [1] "C m0 relative"
#> 
#> $A_m1_relative
#> [1] "A m1 relative"
#> 
#> $B_m1_relative
#> [1] "B m1 relative"
#> 
#> $C_m1_relative
#> [1] "C m1 relative"


来源:https://stackoverflow.com/questions/59355653/apply-a-function-to-a-parameter-grid-and-return-a-list-of-lists-in-purrr

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!