问题描述
我想使用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,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,var2){
furrr::future_map_dfr(.x = data,.f = ~ .x %>%
dplyr::mutate(product = {{var1}} * {{var2}})
)
}
future::plan(multiprocess)
multiplier_parallel(input,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,.f = ~ .x %>%
dplyr::mutate(product = a * b)
)
}
multiplier_parallel(input,b)
到目前为止,我已经尝试了几种方法,包括为.future_options提供名称,但是似乎都没有用。有什么办法可以使这项工作吗?我的实际功能要复杂得多,但是我假设原理是相同的。如果有人可以帮助,那就太好了!
解决方法
future
尝试自动确定您在代码中使用的全局变量。由于评估工作整洁,因此可以识别a
和b
,但找不到。您可以使用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,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
,
从最底层看,这似乎是globals包的错误,furrr使用它来查找需要导出到worker的全局变量。我已在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)
,
啊,在我以前的回答中,我忘记了这是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,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,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()
拥抱模式,但是在像这样的极少数情况下,需要分离化/强制性。