R foreach() - как определить функцию объединения для нескольких выходов при использовании кусков itertools?

Как я могу написать функцию объединения для оператора R foreach(), который использует разбиение itertools, так что я получаю тот же результат, что и использование оператора R foreach() без разбиения itertools?

У меня есть оператор R foreach(), который выполняет вычисления и возвращает список из трех списков. Упрощенная версия, которая дает желаемые результаты, приведена ниже в первом блоке кода - она ​​использует функцию объединения, которую я обнаружил в разделе Сохранение нескольких выходов цикла foreach.

Теперь я хочу запустить этот же код, используя chunking от itertools. Я пробовал это двумя разными способами (см. Второй и третий кодовые блоки ниже), и ни один из них не дал желаемого результата. Проблема состоит в том, что вместо трех списков, состоящих из 3 списков по 10 списков, в обеих моих попытках включить разбиение на itertools три_листы состоят из 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)

1 ответ

Решение

Идея в том, что вы можете использовать .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)
Другие вопросы по тегам