rmarkdown :: проблема рендеринга при вызове из пакета - PullRequest
0 голосов
/ 21 ноября 2018

Я сделал небольшой пакет, чтобы воспроизвести проблему:

# example package
devtools::install_github("privefl/minipkg")

# example Rmd
rmd <- system.file("extdata", "Matrix.Rmd", package = "minipkg")
writeLines(readLines(rmd))  ## see content

# works fine
rmarkdown::render(
  rmd,
  "all",
  envir = new.env(),
  encoding = "UTF-8"
)

# !! does not work !!
minipkg::my_render(rmd)
minipkg::my_render  ## see source code

Я не понимаю, почему поведение отличается и как это исправить.

Править: Я знаю, что могу использовать Matrix::t().Мой вопрос звучит так: «Почему мне нужно использовать его в данном конкретном случае, а не во всех других случаях (например, вызов rmarkdown::render() вне пакета)?».


Ошибка

Quitting from lines 10-13 (Matrix.Rmd) 
Error in t.default(mat) : argument is not a matrix

Файл Matrix.Rmd

---
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

```{r}
library(Matrix)
mat <- rsparsematrix(10, 10, 0.1)
t(mat)
```

Вывод на консоль:

> # example package
> devtools::install_github("privefl/minipkg")
Downloading GitHub repo privefl/minipkg@master
✔  checking for file ‘/private/var/folders/md/03gdc4c14z18kbqwpfh4jdfc0000gr/T/RtmpKefs4h/remotes685793b9df4/privefl-minipkg-c02ae62/DESCRIPTION’ ...
─  preparing ‘minipkg’:
✔  checking DESCRIPTION meta-information ...
─  checking for LF line-endings in source and make files and shell scripts
─  checking for empty or unneeded directories
─  building ‘minipkg_0.1.0.tar.gz’

* installing *source* package ‘minipkg’ ...
** R
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
* DONE (minipkg)
> # example Rmd
> rmd <- system.file("extdata", "Matrix.Rmd", package = "minipkg")
> writeLines(readLines(rmd))  ## see content
---
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

```{r}
library(Matrix)
mat <- rsparsematrix(10, 10, 0.1)
t(mat)
```

> # works fine
> rmarkdown::render(
+   rmd,
+   "all",
+   envir = new.env(),
+   encoding = "UTF-8"
+ )


processing file: Matrix.Rmd
  |.............                                                    |  20%
  ordinary text without R code

  |..........................                                       |  40%
label: setup (with options) 
List of 1
 $ include: logi FALSE

  |.......................................                          |  60%
  ordinary text without R code

  |....................................................             |  80%
label: unnamed-chunk-1
  |.................................................................| 100%
  ordinary text without R code


output file: Matrix.knit.md

/usr/local/bin/pandoc +RTS -K512m -RTS Matrix.utf8.md --to html4 --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash+smart --output Matrix.html --email-obfuscation none --self-contained --standalone --section-divs --template /Library/Frameworks/R.framework/Versions/3.5/Resources/library/rmarkdown/rmd/h/default.html --no-highlight --variable highlightjs=1 --variable 'theme:bootstrap' --include-in-header /var/folders/md/03gdc4c14z18kbqwpfh4jdfc0000gr/T//RtmpKefs4h/rmarkdown-str68525040df1.html --mathjax --variable 'mathjax-url:https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' --metadata pagetitle=Matrix.utf8.md 

Output created: Matrix.html
> # !! does not work !!
> minipkg::my_render(rmd)


processing file: Matrix.Rmd
  |.............                                                    |  20%
  ordinary text without R code

  |..........................                                       |  40%
label: setup (with options) 
List of 1
 $ include: logi FALSE

  |.......................................                          |  60%
  ordinary text without R code

  |....................................................             |  80%
label: unnamed-chunk-1
Quitting from lines 10-13 (Matrix.Rmd) 
Error in t.default(mat) : argument is not a matrix

> minipkg::my_render  ## see source code
function (rmd) 
{
    rmarkdown::render(rmd, "all", envir = new.env(), encoding = "UTF-8")
}
<bytecode: 0x7f89c416c2a8>
<environment: namespace:minipkg>
>

1 Ответ

0 голосов
/ 23 ноября 2018

Как это работает

Проблема в 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 узел.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...