Как пакет foreach распространяется на R Environments при использовании as.formula, SE dplyr и lapply?

У меня есть функция, в которой я динамически создаю несколько формул в виде строк и привожу их к формулам с as.formula. Затем я вызываю эту функцию в параллельном процессе, используя doSNOW и foreach, и использую эти формулы через dplyr::mutate_.

Когда я использую lapply(formula_list, as.formula), я получаю ошибку could not find function *custom_function* при параллельном запуске, хотя он отлично работает при локальном запуске. Однако, когда я использую lapply(formula_list, function(x) as.formula(x), он работает как параллельно, так и локально.

Почему? Каков правильный способ понимания окружения и "правильный" способ его кодирования?

Я получаю предупреждение, которое говорит: In e$fun(obj, substitute(ex), parent.frame(), e$data) : already exporting variable(s): *custom_func*

Минимальный воспроизводимый пример приведен ниже.

# Packages
library(dplyr)
library(doParallel)
library(doSNOW)
library(foreach)

# A simple custom function
  custom_sum <- function(x){
    sum(x)
  } 

# Functions that call create formulas and use them with nse dplyr:
  dplyr_mut_lapply_reg <- function(df){
    my_dots <- setNames(
      object = lapply(list("~custom_sum(Sepal.Length)"), as.formula),
      nm     = c("Sums")
    )

    return(
      df %>%
      group_by(Species) %>%
      mutate_(.dots = my_dots)
    )
  }

  dplyr_mut_lapply_lambda <- function(df){
    my_dots <- setNames(
      object = lapply(list("~custom_sum(Sepal.Length)"), function(x) as.formula(x)),
      nm     = c("Sums")
   )

    return(
      df %>%
      group_by(Species) %>%
      mutate_(.dots = my_dots)
   )
 }

#1. CALLING BOTH LOCALLY
dplyr_mut_lapply_lambda(iris) #works
dplyr_mut_lapply_reg(iris) #works

#2. CALLING IN PARALLEL
  #Faux Parallel Setup
  cl <- makeCluster(1, outfile="")
  registerDoSNOW(cl)

  # Call Lambda Version WORKS
  foreach(j = 1,
          .packages = c("dplyr", "tidyr"),
          .export   = lsf.str()
          ) %dopar% {
     dplyr_mut_lapply_lambda(iris) 
  }



  # Call Regular Version FAILS
  foreach(j = 1,
          .packages = c("dplyr", "tidyr"),
          .export   = lsf.str()
          ) %dopar% {
     dplyr_mut_lapply_reg(iris) 
  }

  # Close Cluster
  stopCluster(cl)   

РЕДАКТИРОВАТЬ: В своем первоначальном заголовке сообщения я написал, что использую nse, но я действительно имел в виду стандартную оценку. Упс. Я изменил это соответственно.


person bigfoot56    schedule 21.06.2017    source источник


Ответы (1)


У меня нет точного ответа, почему здесь, но пакет future (я я автор) обрабатывает такие "хитрые" глобальные переменные - они сложны, потому что не являются частью пакета и являются вложенными, т.е. одна глобальная вызывает другую глобальную. Например, если вы используете:

library("doFuture")
cl <- parallel::makeCluster(1, outfile = "")
plan(cluster, workers = cl)
registerDoFuture()

этот проблемный случай «Call Regular Version FAILS» теперь должен работать.

Теперь в приведенном выше примере используется parallel::makeCluster(), который по умолчанию равен type = "PSOCK", тогда как если вы загрузите doSNOW, вы получите snow::makeCluster(), который по умолчанию равен type = "MPI". К сожалению, полный бэкэнд MPI еще не реализован для будущего пакета. Таким образом, если вы ищете решение для MPI, это вам не поможет (пока).

person HenrikB    schedule 24.06.2017
comment
Это выглядит супер полезно! Я обязательно рассмотрю это еще немного, хотя оставлю вопрос открытым, чтобы, надеюсь, лучше понять основную проблему. - person bigfoot56; 26.06.2017