2017-06-21 2 views
0

J'ai une fonction où je crée dynamiquement plusieurs formules en tant que chaînes et les transpose en formules avec as.formula. J'appelle alors cette fonction dans un processus parallèle en utilisant doSNOW et foreach et utilise ces formules par dplyr::mutate_.Comment fonctionne chaque environnement de classe R pour chaque package lors de l'utilisation de as.formula, SE dplyr et lapply?

Lorsque j'utilise lapply(formula_list, as.formula), j'obtiens l'erreur could not find function *custom_function* lorsqu'elle est exécutée en parallèle, bien qu'elle fonctionne correctement lorsqu'elle est exécutée localement. Cependant, quand j'utilise lapply(formula_list, function(x) as.formula(x) cela fonctionne à la fois en parallèle et localement.

Pourquoi? Quelle est la bonne façon de comprendre les environnements ici et la «bonne» façon de le coder?

je reçois un avertissement qui dit: In e$fun(obj, substitute(ex), parent.frame(), e$data) : already exporting variable(s): *custom_func*

Un exemple reproductible minimale est inférieure.

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

EDIT: Dans le titre de mon message original j'ai écrit que j'utilisais NSE, mais je voulais vraiment dire utiliser l'évaluation standard. Oups. J'ai changé cela en conséquence.

Répondre

1

Je n'ai pas de réponse précise à pourquoi ici, mais le paquet future (je suis l'auteur) gère ce type de globaux "difficiles" - ils sont difficiles parce qu'ils ne font pas partie d'un paquet et ils sont imbriqué, c'est-à-dire qu'un global appelle un autre global. Par exemple, si vous utilisez:

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

le problème «Appelez la version régulière FAILS» doit maintenant fonctionner.

Maintenant, l'utilise ci-dessus parallel::makeCluster() qui par défaut type = "PSOCK", alors que si vous chargez doSNOW vous obtenez snow::makeCluster() qui par défaut type = "MPI". Malheureusement, un MPI backend is yet not implemented complet pour le futur paquet. Ainsi, si vous cherchez une solution MPI, cela ne vous aidera pas (encore).

+0

Cela semble super utile! Je vais certainement étudier la question plus en détail, mais je vais laisser la question ouverte afin que je puisse mieux comprendre le problème fondamental. – bigfoot56