使用带有 furrr 的 tidy 评估
Using tidy evaluations with furrr
我想使用 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 提供名称,但 none 似乎有效。有什么办法可以使这项工作?我的实际功能要复杂得多,但我认为原理是一样的。如果有人能提供帮助就太好了!
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, 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
在最低级别上,这似乎是 globals 包的一个错误,furrr 使用它来查找需要导出给 worker 的全局变量。我已经在 https://github.com/HenrikBengtsson/globals/issues/65
上游报告了这个错误
这个问题与 NSE(非标准评估)以及全局变量“看起来”在哪里找到全局变量有关,并且可以仅使用全局变量和基数 R 进行重现。对于 globals 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 的“常见陷阱”之一。以前的答案不一定不正确,并且提供了一些额外的见解,所以我将保留它。有关详细信息,请参阅此 post 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()
,但在一些非常罕见的情况下,例如这种情况,需要 defusing/forcing 的分隔。
我想使用 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 提供名称,但 none 似乎有效。有什么办法可以使这项工作?我的实际功能要复杂得多,但我认为原理是一样的。如果有人能提供帮助就太好了!
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, 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
在最低级别上,这似乎是 globals 包的一个错误,furrr 使用它来查找需要导出给 worker 的全局变量。我已经在 https://github.com/HenrikBengtsson/globals/issues/65
上游报告了这个错误这个问题与 NSE(非标准评估)以及全局变量“看起来”在哪里找到全局变量有关,并且可以仅使用全局变量和基数 R 进行重现。对于 globals 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 的“常见陷阱”之一。以前的答案不一定不正确,并且提供了一些额外的见解,所以我将保留它。有关详细信息,请参阅此 post 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()
,但在一些非常罕见的情况下,例如这种情况,需要 defusing/forcing 的分隔。