R foreach() - 如何在使用 itertools 块时为多个输出定义组合函数?

R foreach() - how to define combine function for multiple outputs when using itertools chunks?

如何为使用 itertools 分块的 R foreach() 语句编写组合函数,以便获得与使用没有 itertools 分块的 R foreach() 语句相同的结果?

我有一个执行计算的 R foreach() 语句和 returns 一个包含三个列表的列表。下面第一个代码块中提供了一个提供所需输出的简化版本——它使用了我在 Saving multiple outputs of foreach dopar loop 处找到的组合函数。

现在我想 运行 使用 itertools 中的分块来处理相同的代码。我尝试了这两种不同的方式(参见下面的第二个和第三个代码块),但都没有产生预期的结果。问题是 three_lists 不是由 10 个列表的 3 个列表组成,而是在我合并 itertools 分块的两次尝试中,three_lists 是由 2 个列表的 3 个列表组成(2 个列表的长度不同在不同的尝试中)。我猜测列表的长度为 2 而不是 10,因为 num_cores 在我的计算机上是 2 - 这向我暗示我的组合函数可能需要更改以在使用 itertools 分块时正确组合输出。不过,我无法弄清楚如何更改它。我该如何更改组合功能?

这是生成所需结果的 foreach() 语句:

# set up
library(foreach)
library(doParallel)

# set parallel options
num_cores_total <- detectCores() 
num_cores <- num_cores_total - 2
cl <- makeCluster(spec= num_cores, type="PSOCK")
registerDoParallel(cl, cores = num_cores)

# create function that will separate out foreach output into list of three lists
comb <- function(x, ...) {
  lapply(seq_along(x),
         function(i) c(x[[i]], lapply(list(...), function(y) y[[i]])))
}

# foreach statement
three_lists <- foreach(i = 1:10, .inorder=TRUE, .combine='comb', .multicombine=TRUE, .init=list(list(), list(), list())) %dopar% {

  first_output <- i*1
  second_output <- i*10
  third_output <- i*100

  list(first_output, second_output, third_output)

}

first_output_list <- three_lists[[1]]
second_output_list <- three_lists[[2]]
third_output_list <- three_lists[[3]]

这是我第一次(未成功)尝试将 itertools 分块合并到代码中:

# set up
library(foreach)
library(itertools)
library(doParallel)

# set parallel options
num_cores_total <- detectCores() 
num_cores <- num_cores_total - 2
cl <- makeCluster(spec= num_cores, type="PSOCK")
registerDoParallel(cl, cores = num_cores)

# create function that will separate out foreach output into list of three lists
comb <- function(x, ...) {
  lapply(seq_along(x),
         function(i) c(x[[i]], lapply(list(...), function(y) y[[i]])))
}

# foreach statement
three_lists <- foreach(thisIter=isplitIndices(10, chunks=num_cores), .inorder=TRUE, .combine='comb', .multicombine=TRUE, .init=list(list(), list(), list())) %dopar% {

    first_output <- thisIter*1
    second_output <- thisIter*10
    third_output <- thisIter*100

    list(first_output, second_output, third_output)

}

first_output_list <- three_lists[[1]]
second_output_list <- three_lists[[2]]
third_output_list <- three_lists[[3]]


# stop cluster
stopCluster(cl)

这是我第二次(未成功)尝试将 itertools 分块合并到代码中:

# set up
library(foreach)
library(itertools)
library(doParallel)

# set parallel options
num_cores_total <- detectCores() 
num_cores <- num_cores_total - 2
cl <- makeCluster(spec= num_cores, type="PSOCK")
registerDoParallel(cl, cores = num_cores)

# create function that will separate out foreach output into list of three lists
comb <- function(x, ...) {
  lapply(seq_along(x),
         function(i) c(x[[i]], lapply(list(...), function(y) y[[i]])))
}

# foreach statement
three_lists <- foreach(thisIter=isplitIndices(10, chunks=num_cores), .inorder=TRUE, .combine='comb', .multicombine=TRUE, .init=list(list(), list(), list())) %dopar% {

  calc_function <- function(x){
    first_output <- x*1
    second_output <- x*10
    third_output <- x*100

    return(list(first_output, second_output, third_output))
  }

  sapply(thisIter, calc_function)  
}

first_output_list <- three_lists[[1]]
second_output_list <- three_lists[[2]]
third_output_list <- three_lists[[3]]

# stop cluster
stopCluster(cl)

想法是您可以使用 .combine=c 附加以块形式返回的列表 (这样你就不会得到嵌套列表), 然后按照没有 itertools 的方式调整结构 (但简化了一点):

lists <- foreach(thisIter=isplitIndices(10L, chunks=num_cores), .combine=c) %dopar% {
    lapply(thisIter, function(i) {
        c(i * 1L, 
          i * 10L,
          i * 100L)
    })
}

first_output_list <- lapply(lists, "[", 1L)
second_output_list <- lapply(lists, "[", 2L)
third_output_list <- lapply(lists, "[", 3L)