2

我想使用furrr包而不是purrr包使以下函数并行运行。

library(furrr)
library(tidyverse)

input <- list(element1 = tibble::tibble(a = c(1, 2), b = c(2, 2)),
              element2 = tibble::tibble(a = c(1, 2), b = c(4, 4))
)

multiplier <- function(data, var1, var2){
  purrr::map_df(.x = data,
                .f = ~ .x %>% 
                  dplyr::mutate(product = {{var1}} * {{var2}})
  )
}

multiplier(input, a, b)

但是,当我将其转换为furrr等价物时,会出现错误。

multiplier_parallel <- function(data, var1, var2){
  furrr::future_map_dfr(.x = data,
                .f = ~ .x %>% 
                  dplyr::mutate(product = {{var1}} * {{var2}})
  )
}

future::plan(multiprocess)

multiplier_parallel(input, a, b)
Error in get(name, envir = env, inherits = FALSE) : 
Identified global objects via static code inspection (structure(function (..., .x = ..1, .y = ..2, . = 
..1); .x %>% dplyr::mutate(product = {; {; var1; }; } * {; {; var2; }; }), class = 
c("rlang_lambda_function", "function"))). Object 'a' not found 

我认为原因是该future包会查找要导出给工作人员的所有必要变量。在这种情况下,它正在寻找列名“a”作为全局变量,但找不到它,因此出现错误。

当我只是将变量名插入到调用中时,它可以工作,但是现在该函数不再适用于任何变量名:

multiplier_parallel <- function(data, var1, var2){
  furrr::future_map_dfr(.x = data,
                .f = ~ .x %>% 
                  dplyr::mutate(product = a * b)
  )
}

multiplier_parallel(input, a, b)

到目前为止,我尝试了几件事,包括为 .future_options 提供名称,但似乎都没有。有什么办法可以使这项工作?我的实际功能要复杂得多,但我认为主体是相同的。如果有人可以提供帮助,那就太好了!

4

3 回答 3

5

future尝试自动确定您在代码中使用的全局变量。由于整洁的评估,它识别ab没有找到它。您可以使用 禁用此设置future_options(globals = FALSE)

future::plan(future::multiprocess)

input <- list(element1 = tibble::tibble(a = c(1, 2), b = c(2, 2)),
              element2 = tibble::tibble(a = c(1, 2), b = c(4, 4))
)

multiplier_parallel <- function(data, var1, var2){
      furrr::future_map_dfr(.x = data,
                            .f = ~ .x %>% 
                                  dplyr::mutate(product = {{var1}} * {{var2}}),
                            .options = furrr::future_options(globals = FALSE)
      )
}

multiplier_parallel(input, a, b)
# A tibble: 4 x 3
      a     b product
  <dbl> <dbl>   <dbl>
1     1     2       2
2     2     2       4
3     1     4       4
4     2     4       8
于 2020-09-22T08:58:38.967 回答
2

在最低级别,这似乎是 globals 包的一个错误,furrr 使用它来查找需要导出给工作人员的全局变量。我已经在https://github.com/HenrikBengtsson/globals/issues/65向上游报告了这个错误

该问题与 NSE(非标准评估)和全局变量“查找”以查找全局变量的位置有关,并且可以仅使用全局变量和基础 R 进行复制。使用全局变量 0.13.0,我得到以下信息:

library(globals)

fn <- function(expr) {
  expr <- substitute(expr)
  eval(expr, envir = mtcars)
}

fn(cyl)
#>  [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

expr <- quote(fn(cyl))

globalsOf(expr)
#> Error in globalsByName(names, envir = envir, mustExist = mustExist): Identified global objects via static code inspection (fn(cyl)). Failed to locate global object in the relevant environments: 'cyl'

错误消息有点不同,但我相当确定这是同一个潜在问题。

奇怪的是,当列被硬编码时没有发生错误,但我们仍然延迟评估。即这有效,但结果相当长,所以我不会显示输出:

library(globals)

fn <- function() {
  expr <- quote(cyl)
  eval(expr, envir = mtcars)
}

fn()

expr <- quote(fn())

globalsOf(expr)
于 2020-09-22T12:03:22.323 回答
2

啊,在我之前的回答中,我忘记了这是 furrr 的“常见问题”之一。前面的答案不一定是不正确的,并且提供了一些额外的见解,所以我会留下它。有关更多信息,请参阅此帖子https://davisvaughan.github.io/furrr/articles/articles/gotchas.html#non-standard-evaluation-of-arguments

与 purrr 不同,对于 furrr,每个参数都必须提前评估一次才能将其发送给工作人员。这意味着使用 NSE 的参数存在一些差异。实际上,您可以通过首先用 化解参数enquo(),然后用 强制它们在 furrr 函数中求值来解决这个问题!!。提前化解它们会var1变成var2可以运送给工人的物体。

input <- list(
  element1 = tibble::tibble(a = c(1, 2), b = c(2, 2)),
  element2 = tibble::tibble(a = c(1, 2), b = c(4, 4))
)

multiplier_parallel <- function(data, var1, var2) {
  var1 <- rlang::enquo(var1)
  var2 <- rlang::enquo(var2)
  
  furrr::future_map_dfr(
    .x = data,
    .f = ~dplyr::mutate(.x, product = !!var1 * !!var2)
  )
}

future::plan(future::multisession, workers = 2)

multiplier_parallel(input, a, b)
#> # A tibble: 4 x 3
#>       a     b product
#>   <dbl> <dbl>   <dbl>
#> 1     1     2       2
#> 2     2     2       4
#> 3     1     4       4
#> 4     2     4       8

请注意,我们通常鼓励在 上使用{{ }}拥抱模式!!enquo(),但在一些非常罕见的情况下,例如这种情况,需要分离化解/强制。

于 2020-09-22T12:35:15.323 回答