@ben-bolker,我有类似的愿望,并且我已经想出了一个初步的解决方案,我也测试了并行工作。这个函数有些令人困惑地被称为gmcmapply
(g代表网格),它接受任意大小的命名列表mvars
(在函数内部进行了expand.grid
),以及一个FUN
,该函数使用列表名称,就好像它们是函数本身的参数一样(gmcmapply
将更新FUN
的形式,以便在将FUN
传递给mcmapply
时,其参数反映用户希望迭代的变量(这将是嵌套for循环中的层))。然后,mcmapply
在循环遍历mvars
中扩展的变量集时动态更新这些形式的值。
我已经发布了初步代码,链接为
一个 gist(下面有一个示例),很想听听你的反馈。我是一名研究生,自认为是中级水平的 R 爱好者,所以这肯定会考验我的 R 技能。你或其他社区的人可能会有建议,可以改进我所拥有的东西。即使它现在就这样,我也认为将来我会经常使用这个函数。
gmcmapply <- function(mvars, FUN, SIMPLIFY = FALSE, mc.cores = 1, ...){
require(parallel)
FUN <- match.fun(FUN)
funArgs <- formals(FUN)[which(names(formals(FUN)) != "...")]
expand.dots <- list(...)
if(any(names(funArgs) %in% names(expand.dots))){
dot_overwrite <- names(funArgs[which(names(funArgs) %in% names(expand.dots))])
funArgs[dot_overwrite] <- expand.dots[dot_overwrite]
expand.dots[dot_overwrite] <- NULL
}
grid <- expand.grid(mvars,KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE)
argdefs <- rep(list(bquote()), ncol(grid) + length(expand.dots) + length(funArgs) + 1)
names(argdefs) <- c(colnames(grid), names(funArgs), names(expand.dots), "...")
argdefs[which(names(argdefs) %in% names(funArgs))] <- funArgs
argdefs[which(names(argdefs) %in% names(expand.dots))] <- expand.dots
formals(FUN) <- argdefs
if(SIMPLIFY) {
do.call(mcmapply, c(FUN, c(unname(grid), mc.cores = mc.cores)))
} else{
do.call(mcmapply, c(FUN, c(unname(grid), SIMPLIFY = FALSE, mc.cores = mc.cores)))
}
}
以下是示例代码:
myfunc <- function(...){
return_me <- paste(l3, l1^2 + l2, sep = "_")
return(return_me)
}
mvars <- list(l1 = 1:10,
l2 = 1:5,
l3 = letters[1:3])
lreturns <- gmcmapply(mvars, myfunc)
lreturns <- gmcmapply(mvars, myfunc, SIMPLIFY = TRUE)
lreturns <- c()
for(l1 in 1:10){
for(l2 in 1:5){
for(l3 in letters[1:3]){
lreturns <- c(lreturns,myfunc(l1,l2,l3))
}
}
}
lreturns <- gmcmapply(mvars, myfunc, SIMPLIFY = TRUE, mc.cores = 2)
Example 2. Pass non-default args to FUN.
myfunc <- function(rep_letters = 3, ...){
return_me <- paste(rep(l3, rep_letters), l1^2 + l2, sep = "_")
return(return_me)
}
lreturns <- gmcmapply(mvars, myfunc, rep_letters = 1)
我想要增加一些额外的功能,但目前还在努力解决中。
清理输出,使其成为一个漂亮的嵌套列表,并显示mvars的名称(通常情况下,我会在嵌套循环中创建多个列表,并将低级别的列表标记到较高级别的列表,直到完成巨大的嵌套循环的所有层级)。我认为使用提供的解决方案的某些抽象变体here将起作用,但我还没有想出如何使解决方案灵活地适应expand.grid
数据框中的列数。
我希望有一个选项来记录在用户指定的目录中调用mcmapply
中的子进程的输出。因此,您可以查看expand.grid
生成的每个变量组合的.txt输出(即如果用户将模型摘要或状态消息作为FUN
的一部分打印,就像我经常做的那样)。我认为一个可行的解决方案是使用substitute()
和body()
函数,描述了here ,以编辑FUN
来在FUN
开头打开sink()
并在末尾关闭它,如果用户指定要写入的目录。现在,我只是将其编程到FUN
本身中,但以后将为gmcmapply
传递一个名为log_children="path_to_log_dir"
的参数,并编辑函数体以(伪代码)sink(file=file.path(log_children, paste0(paste(names(mvars), sep="_"), ".txt"))
让我知道你的想法!
- Nate
plyr::m*ply
(以及expand.grid
)似乎是一个简单的任务。 - baptisteexpand.grid
可能比我想象的更灵活... - Ben Bolker