Как это работает
Проблема в envir = new.env()
.Вам нужно envir = new.env(parent = globalenv())
:
devtools::install_github("privefl/minipkg")
rmd <- system.file("extdata", "Matrix.Rmd", package = "minipkg")
minipkg::my_render(rmd)
# Fails
f <- minipkg::my_render
body(f) <- quote(rmarkdown::render(rmd, "all", envir = new.env(parent = globalenv()), encoding = "UTF-8"))
ns <- getNamespace("minipkg")
unlockBinding("my_render", ns)
assign("my_render", f, envir = ns)
minipkg::my_render(rmd)
# Patched one works :)
Почему это работает
Посмотрите на аргументы по умолчанию new.env()
, чтобы найти родительскую среду по умолчанию parent.frame()
.Обратите внимание, что из консоли это будет globalenv()
, и из пакета это будут следующие пакеты пространство имен (не то же самое, что окружение пакета!).
Вы можете получить пакетпространство имен с getNamespace("pkg")
.Это среда, которая содержит все (также внутренние) объекты пакета.Проблема в том, что эта среда в некотором смысле «отключена» от обычной механики поиска / поиска методов в R, и поэтому вы не найдете необходимые методы, даже если они подключены к search()
.
.выбор new.env(parent = globalenv())
устанавливает родительскую среду в верхней части пути поиска и, таким образом, может находить все присоединенные методы.
Сравнительный анализ различных подходов
Все эти три подхода создают надлежащие HTML-файлы:
#' Render an Rmd file
#' @param rmd Path of the R Markdown file to render.
#' @export
my_render <- function(rmd) {
rmarkdown::render(
rmd,
"all",
envir = new.env(parent = globalenv()),
encoding = "UTF-8"
)
}
#' Render an Rmd file
#' @param rmd Path of the R Markdown file to render.
#' @export
my_render2 <- function(rmd) {
cl <- parallel::makePSOCKcluster(1)
on.exit(parallel::stopCluster(cl), add = TRUE)
parallel::clusterExport(cl, "rmd", envir = environment())
parallel::clusterEvalQ(cl, {
rmarkdown::render(rmd, "all", encoding = "UTF-8")
})[[1]]
}
#' Render an Rmd file
#' @param rmd Path of the R Markdown file to render.
#' @export
my_render3 <- function(rmd) {
system2(
command = "R",
args = c("-e", shQuote(sprintf("rmarkdown::render('%s', 'all', encoding = 'UTF-8')", gsub("\\\\", "/", normalizePath(rmd))))),
wait = TRUE
)
}
Теперь интересно сравнить их скорость:
> microbenchmark::microbenchmark(my_render("inst/extdata/Matrix.Rmd"), my_render2("inst/extdata/Matrix.Rmd"), my_render3("inst/extdata/Matrix.Rmd"), times = 10L)
[...]
Unit: milliseconds
expr min lq mean median uq max neval
my_render("inst/extdata/Matrix.Rmd") 352.7927 410.604 656.5211 460.0608 560.3386 1836.452 10
my_render2("inst/extdata/Matrix.Rmd") 1981.8844 2015.541 2163.1875 2118.0030 2307.2812 2407.027 10
my_render3("inst/extdata/Matrix.Rmd") 2061.7076 2079.574 2152.0351 2138.9546 2181.1284 2377.623 10
Выводы
envir = new.env(globalenv())
намного быстрее (почти в 4 раза быстрее, чемальтернативы)
Я ожидаю, что издержки будут постоянными, поэтому они не должны иметь значения для больших файлов Rmd. - Нет заметной разницы между порождением нового процесса с
system2
и использованием параллельного кластера SOCK с1 узел.