I have the following Rmd
file I called test.Rmd
:
---
title: "test"
output: html_document
---
```{r}
print(y)
```
```{r}
x <- "don't you ignore me!"
print(x)
```
I want to call render the following way:
render('test.Rmd', output_format = "html_document",
output_file = 'test.html',
envir = list(y="hello"))
but it fails:
processing file: test.Rmd
|................ | 25%
ordinary text without R code
|................................ | 50%
label: unnamed-chunk-1
|................................................. | 75%
ordinary text without R code
|.................................................................| 100%
label: unnamed-chunk-2
Quitting from lines 11-13 (test.Rmd)
Error in print(x) : object 'x' not found
The first chunk went just fine, so something has worked. If I define y
in my global environment I can run it without the envir
argument and it works fine.
I figured maybe render
doesn't like lists, so let's give it a proper environment :
y_env <- as.environment(list(y="hello"))
ls(envir = y_env)
# [1] "y"
render('test.Rmd', output_format = "html_document",
output_file = 'test.html',
envir = y_env)
But it's even worse, it doesn't find print
!
processing file: test.Rmd
|................ | 25%
ordinary text without R code
|................................ | 50%
label: unnamed-chunk-1
Quitting from lines 7-8 (test.Rmd)
Error in eval(expr, envir, enclos) : could not find function "print"
Now the docs mentions using the function new.env
so out of despair I try this :
y_env <- new.env()
y_env$y <- "hello"
render('test.Rmd', output_format = "html_document",
output_file = 'test.html',
envir = y_env)
And now it works!
processing file: test.Rmd
|................ | 25%
ordinary text without R code
|................................ | 50%
label: unnamed-chunk-1
|................................................. | 75%
ordinary text without R code
|.................................................................| 100%
label: unnamed-chunk-2
output file: test.knit.md
"C:/Program Files/RStudio/bin/pandoc/pandoc" +RTS -K512m -RTS test.utf8.md --to html --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash --output test.html --smart --email-obfuscation none --self-contained --standalone --section-divs --template "**redacted**\RMARKD~1\rmd\h\DEFAUL~1.HTM" --no-highlight --variable highlightjs=1 --variable "theme:bootstrap" --include-in-header "**redacted**\AppData\Local\Temp\RtmpGm9aXz\rmarkdown-str3f6c5101cb3.html" --mathjax --variable "mathjax-url:https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
Output created: test.html
So I'm confused about several things, to recap :
render
recognize lists (first chunk didn't fail) but then ignores regular assignments in the chunksThe latter ( envir) offers a way to render a document with the guarantee of an empty new environment when you call rmarkdown::render (..., envir = new.env ()), so objects created in your code chunks will stay inside this environment, without polluting your current global environment.
Rmarkdown is useful tool for generating data-driven documents in the R environment. Given that markdown documents can be rendered to several formats (html, docx, etc.), you can use.Rmd files as a substitute for shiny UIs. In other words, you can write in Rmarkdown and let the shiny server render your file (s) into HTML documents.
Rmarkdown is useful tool for generating data-driven documents in the R environment. Given that markdown documents can be rendered to several formats (html, docx, etc.), you can use .Rmd files as a substitute for shiny UIs. In other words, you can write in Rmarkdown and let the shiny server render your file (s) into HTML documents.
I will try the cache =TRUE option. Going to RStudio´s 'Tools' and 'Global options' and visiting the 'R Markdown' tab, you can make a selection in 'Evaluate chunks in directory', there select the option 'Documents' and the R Markdown knitting engine will be accessing the global environment as plain R code does.
Your two first examples fail for different reasons. To understand both failures, it's first important to know a bit about how code chunks are evaluated by knitr and rmarkdown.
When you call rmarkdown::render()
on your file, each code chunk is ultimately evaluated by a call to evaluate::evaluate()
. In terms of its evaluation behavior and scoping rules, evaluate()
behaves almost exactly like the base R function eval()
.
(Where evaluate::evaluate()
differs most from eval()
is in how it handles the output of each evaluated expression. As explained in ?evaluate
, in addition to evaluating the expression passed as its first argument, it "captures all of the information necessary to recreate the output as if you had copied and pasted the code into an R terminal". That info includes plots and warning and error messages, which is why it's so handy in a package like knitr!)
In any case, the eventual call to evaluate()
, from within the function knitr:::block_exec()
, looks something like this
evaluate::evaluate(code, envir = env, ...)
in which:
code
is a vector of character strings giving the (possibly multiple) expressions making up the current chunk.
env
is value that you supplied the envir
formal argument in your original call to rmarkdown::render()
.
In your first example, envir
is a list, not an environment. When that is the case, evaluation is carried out in a local environment created by the function call. Unresolved symbols (as documented in both ?eval
and ?evaluate
) are looked for first in the list passed a envir
and then in the chain of environments beginning with that given by the enclos
argument. Assignments, crucially, are local to the temporary evaluation environment, which goes out of existence once the function call is complete.
Because evaluate()
operates, one at a time, on a character vector of expressions, when envir
is a list, variables created in one of those expression won't be available for use in the subsequent expressions.
When the envir
argument to rmarkdown::render()
is a list, your code block ultimately gets evaluated by a call like this:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- list(y = 1:10)
evaluate(code, envir = env)
## Or, for prettier printing:
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## Error in print(x): object 'x' not found
The effect is exactly the same as if you did this with eval()
:
env <- list(y =1 :10)
eval(quote(x <- "don't you ignore me"), envir = env)
eval(quote(x), envir = env)
## Error in eval(quote(x), envir = env) : object 'x' not found
When envir=
is an environment returned by as.environment(list())
, you get errors for a different reason. In that case, your code block ultimately gets evaluated by a call like this:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- as.environment(list(y = 1:10))
evaluate(code, envir = env)
## Or, for prettier printing:
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## Error in x <- "don't you ignore me!": could not find function "<-"
## > print(x)
## Error in print(x): could not find function "print"
As you've noted, this fails because as.environment()
returns an environment whose enclosing environment is the empty environment (i.e. the environment returned by emptyenv()
). evaluate()
(like eval()
would) looks for the symbol <-
in env
and, when it doesn't find it there, starts up the chain of enclosing environments which, here, don't contain any match. (Recall also that when envir
is an environment, rather than a list, the enclos
argument is not used.)
To do what you want, you'll need to create an environment that: (1) contains all of the objects in your list and that; (2) has as its enclosing environment the parent environment of your call to render()
(i.e. the environment in which a call to render()
is normally evaluated). The most succinct way to do that is to use the nifty list2env()
function, like so:
env <- list2env(list(y="hello"), parent.frame())
render('test.Rmd', output_format = "html_document",
output_file = 'test.html',
envir = env)
Doing so will result in your code chunks being evaluated by code like the following, which is what you want:
library(evaluate)
code <- c('x <- "don\'t you ignore me!"',
'print(x)')
env <- list2env(list(y = 1:10), envir = parent.frame())
evaluate(code, envir = env)
replay(evaluate(code, envir = env))
## > x <- "don't you ignore me!"
## > print(x)
## [1] "don't you ignore me!"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With