【问题标题】:apply a function to a parameter grid and return a list of lists in purrr将函数应用于参数网格并返回 purrr 中的列表列表
【发布时间】:2019-12-16 11:23:01
【问题描述】:

我经常使用列表列表将函数(通常是模型调用)应用于参数网格。

这是一个以paste为终极功能的例子:

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

我正在寻找一种通过更简单的调用获得完全相同结果的方法,可能是使用crossexpand.gridpmapat_depth

pmap + expand.grid 我得到了一些有趣的东西,但它使结构变平并删除了名称:

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

在最好的情况下,该函数甚至可以在 map 调用中使用名称 (variable, model, type)(而不是 ..1,..2,..3 用于 pmap)。

有没有办法得到这个?

【问题讨论】:

  • variable %>% set_names %>% map(~ model) %>% map(set_names) %>% map_depth(2,~ type) %>% map_depth(2, set_names) 得到你想要的结构,但添加 map_depth(3, cross3(type, model, variable) %>% map(rev) %>% map(paste, collapse=" ")) 不起作用,原因不明

标签: r purrr


【解决方案1】:

据我了解,您需要根据参数组合评估函数将结果保存在由参数组合分层定义的嵌套列表结构中。使用带有递归的函数,一种方法是:

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)
}

上述函数为每个参数组合返回一个未评估的调用(为了示例;在实际使用中do.call(FUN, collect) 应该替换as.call(c(FUN, collect)))。例如:

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")

与示例比较:

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】:

    这似乎是你想要的

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

    这给了

    #> $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"
    

    【讨论】:

    • 感谢您的尝试,但这提供了一个简单的平面列表,就像我的 expand.grid + map 示例一样(尽管您的答案更好地保留了名称,但它提供了类似的输出)。我明确表示我不想展平结构,而是想要一个列表列表(列表列表......)作为输出。
    猜你喜欢
    • 1970-01-01
    • 2012-11-08
    • 2018-12-19
    • 1970-01-01
    • 1970-01-01
    • 2013-12-14
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多