R 仅在本地范围内加载库
R load libraries only in the local scope
是否可以 library()
只影响局部范围?
例如,我的脚本有一个 lapply
循环,其他脚本中的 source() 代码;这些脚本加载它们自己的库,但我希望在执行这些脚本中的代码后清理命名空间,以便脚本 1 调用的包 A 中的函数不会屏蔽脚本 2 调用的包 B 中的函数。
我知道有像 modules
和 import
包这样的完整解决方案,但我想要一个更简单的基础 R 解决方案,仅在需要时才进行扩展。
使用loadNamespace("pkg")
将加载'pkg' 包的名称空间。使用 library("pkg")
还将 附加 'pkg' 包到 R 的搜索路径。您可以使用 loadedNamespaces()
查看加载了哪些命名空间,使用 search()
附加了哪些包。这些属性对于 R 来说是全局的。换句话说,将它们设为本地是不可能的。
如果您希望在 function/map-reduce 调用中使用 library()
而不会 library()
导致包附加到您当前的 R 会话中,那么,正如其他人所建议的,您可以评估您在外部 R 进程中的调用。
(免责声明:我是作者)我建议使用期货(future) for this where you evaluate them external via future.callr - a future wrapper for callr。未来的框架将负责导出外部 R 进程所需的对象。
这是一个例子:
library(future)
plan(future.callr::callr, workers = 1)
y <- lapply((1:3)/4, FUN = function(x) value(future({
library(gtools)
logit(x)
})))
注意如何创建 future 并立即检索其值,即 value(future(...))
。为了澄清这个事实,您可以使用:
eval_via_future <- function(expr, substitute = TRUE, envir = parent.frame()) {
if (substitute) expr <- substitute(expr)
f <- future::future(expr, substitute = FALSE, envir = envir)
future::value(f)
}
library(future)
plan(future.callr::callr, workers = 1)
y <- lapply((1:3)/4, FUN = function(x) eval_via_future({
library(gtools)
logit(x)
})))
如果你想避免让用户指定 plan()
,你可以这样做:
eval_via_callr <- function(expr, substitute = TRUE, envir = parent.frame()) {
oplan <- future::plan()
on.exit(future::plan(oplan))
future::plan(future.callr::callr, workers = 1)
if (substitute) expr <- substitute(expr)
f <- future::future(expr, substitute = FALSE, envir = envir)
future::value(f)
}
所以你可以这样做:
y <- lapply((1:3)/4, FUN = function(x) eval_via_callr({
library(gtools)
logit(x)
}))
无需加载或附加 gtools(它显然会加载 future 和 callr[=53= 所需的其他包]);
> loadedNamespaces()
[1] "codetools" "grDevices" "listenv" "future" "ps"
[6] "memuse" "clisymbols" "prompt" "digest" "crayon"
[11] "rappdirs" "R6" "future.callr" "datasets" "utils"
[16] "callr" "graphics" "base" "tools" "parallel"
[21] "compiler" "processx" "stats" "globals" "methods"
> search()
[1] ".GlobalEnv" "package:stats" "package:graphics"
[4] "package:grDevices" "package:utils" "package:datasets"
[7] "CBC tools" "toolbox:default" "package:methods"
[10] "Autoloads" "package:base"
更新 2020-07-26
以下是如何通过 future 在外部 R 进程中获取 R 脚本,同时从主 R 会话中提取全局变量:
future_source <- function(file, envir = parent.frame(), ...) {
expr <- parse(file = file, keep.source = FALSE)
expr <- bquote({..(expr)}, splice = TRUE)
future::future(expr, substitute = FALSE, envir = envir, ...)
}
source_via_callr <- function(file, envir = parent.frame()) {
oplan <- future::plan()
on.exit(future::plan(oplan))
future::plan(future.callr::callr, workers = 1)
f <- future_source(file, envir = envir)
future::value(f)
}
## Two R scripts
cat("log(a)\n", file="a.R")
cat("library(gtools)\nlogit(a)\n", file="b.R")
## A global
a <- 0.42
## Source scripts in external process
y <- lapply(c("a.R", "b.R"), FUN = source_via_callr)
是否可以 library()
只影响局部范围?
例如,我的脚本有一个 lapply
循环,其他脚本中的 source() 代码;这些脚本加载它们自己的库,但我希望在执行这些脚本中的代码后清理命名空间,以便脚本 1 调用的包 A 中的函数不会屏蔽脚本 2 调用的包 B 中的函数。
我知道有像 modules
和 import
包这样的完整解决方案,但我想要一个更简单的基础 R 解决方案,仅在需要时才进行扩展。
使用loadNamespace("pkg")
将加载'pkg' 包的名称空间。使用 library("pkg")
还将 附加 'pkg' 包到 R 的搜索路径。您可以使用 loadedNamespaces()
查看加载了哪些命名空间,使用 search()
附加了哪些包。这些属性对于 R 来说是全局的。换句话说,将它们设为本地是不可能的。
如果您希望在 function/map-reduce 调用中使用 library()
而不会 library()
导致包附加到您当前的 R 会话中,那么,正如其他人所建议的,您可以评估您在外部 R 进程中的调用。
(免责声明:我是作者)我建议使用期货(future) for this where you evaluate them external via future.callr - a future wrapper for callr。未来的框架将负责导出外部 R 进程所需的对象。 这是一个例子:
library(future)
plan(future.callr::callr, workers = 1)
y <- lapply((1:3)/4, FUN = function(x) value(future({
library(gtools)
logit(x)
})))
注意如何创建 future 并立即检索其值,即 value(future(...))
。为了澄清这个事实,您可以使用:
eval_via_future <- function(expr, substitute = TRUE, envir = parent.frame()) {
if (substitute) expr <- substitute(expr)
f <- future::future(expr, substitute = FALSE, envir = envir)
future::value(f)
}
library(future)
plan(future.callr::callr, workers = 1)
y <- lapply((1:3)/4, FUN = function(x) eval_via_future({
library(gtools)
logit(x)
})))
如果你想避免让用户指定 plan()
,你可以这样做:
eval_via_callr <- function(expr, substitute = TRUE, envir = parent.frame()) {
oplan <- future::plan()
on.exit(future::plan(oplan))
future::plan(future.callr::callr, workers = 1)
if (substitute) expr <- substitute(expr)
f <- future::future(expr, substitute = FALSE, envir = envir)
future::value(f)
}
所以你可以这样做:
y <- lapply((1:3)/4, FUN = function(x) eval_via_callr({
library(gtools)
logit(x)
}))
无需加载或附加 gtools(它显然会加载 future 和 callr[=53= 所需的其他包]);
> loadedNamespaces()
[1] "codetools" "grDevices" "listenv" "future" "ps"
[6] "memuse" "clisymbols" "prompt" "digest" "crayon"
[11] "rappdirs" "R6" "future.callr" "datasets" "utils"
[16] "callr" "graphics" "base" "tools" "parallel"
[21] "compiler" "processx" "stats" "globals" "methods"
> search()
[1] ".GlobalEnv" "package:stats" "package:graphics"
[4] "package:grDevices" "package:utils" "package:datasets"
[7] "CBC tools" "toolbox:default" "package:methods"
[10] "Autoloads" "package:base"
更新 2020-07-26
以下是如何通过 future 在外部 R 进程中获取 R 脚本,同时从主 R 会话中提取全局变量:
future_source <- function(file, envir = parent.frame(), ...) {
expr <- parse(file = file, keep.source = FALSE)
expr <- bquote({..(expr)}, splice = TRUE)
future::future(expr, substitute = FALSE, envir = envir, ...)
}
source_via_callr <- function(file, envir = parent.frame()) {
oplan <- future::plan()
on.exit(future::plan(oplan))
future::plan(future.callr::callr, workers = 1)
f <- future_source(file, envir = envir)
future::value(f)
}
## Two R scripts
cat("log(a)\n", file="a.R")
cat("library(gtools)\nlogit(a)\n", file="b.R")
## A global
a <- 0.42
## Source scripts in external process
y <- lapply(c("a.R", "b.R"), FUN = source_via_callr)