代码之家  ›  专栏  ›  技术社区  ›  moodymudskipper

在受限环境中使用“rmarkdown::render”

  •  11
  • moodymudskipper  · 技术社区  · 6 年前

    我有以下几点 Rmd 我打电话给的文件 test.Rmd

    ---
    title: "test"
    output: html_document
    ---
    
    ```{r}
    print(y)
    ```
    
    ```{r}
    x <- "don't you ignore me!"
    print(x)
    ```
    

    我想按以下方式调用渲染:

    render('test.Rmd', output_format = "html_document",
            output_file = 'test.html',
            envir = list(y="hello"))
    

    但它失败了:

    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
    

    y envir 而且效果很好。

    render 不喜欢列表,所以让我们给它一个合适的环境:

    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)
    

    但更糟的是,它找不到 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"
    

    现在文档提到了使用这个函数 new.env

    y_env <- new.env()
    y_env$y <- "hello"
    render('test.Rmd', output_format = "html_document",
           output_file = 'test.html',
           envir = y_env)
    

    现在它成功了!

    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
    

    所以我对几件事感到困惑,概括一下:

    • 为什么会这样 提供
    • 为什么我的第二次尝试不起作用,它与我的第三次尝试有何不同?
    • 这是虫子吗?
    1 回复  |  直到 6 年前
        1
  •  10
  •   Josh O'Brien    6 年前

    你的前两个例子失败的原因不同。要理解这两种失败,首先需要了解代码块是如何由 R标记 .


    knitr的通用代码块评估过程

    当你打电话的时候 rmarkdown::render() 在您的文件中,每个代码块最终都通过调用 evaluate::evaluate() evaluate() 其行为几乎与基R函数完全相同 eval()

    (其中 evaluate::evaluate() 最不同于 评估() 它如何处理每个计算表达式的输出。如中所述 ?evaluate 针织衫 !)

    无论如何,最终 评估() knitr:::block_exec() ,看起来像这样

    evaluate::evaluate(code, envir = env, ...)
    

    • code 是字符串向量,给出构成当前块的表达式(可能有多个)。

    • env 是您提供的值 envir 你最初呼吁 rmarkdown::render() .


    在你的第一个例子中, 环境 是一个列表,而不是一个环境。在这种情况下,将在函数调用创建的本地环境中执行求值。未解析符号(如两个 ?eval )都是在名单上找第一个通过的 环境 然后在由 enclos

    因为 评估() 每次对表达式的字符向量操作一个,当 环境 是一个列表,在其中一个表达式中创建的变量将无法在后续表达式中使用。

    环境 论据 是一个列表,您的代码块最终通过如下调用进行计算:

    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
    

    :

    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
    

    envir= 是由返回的环境 as.environment(list()) ,则会因不同的原因出现错误。在这种情况下,您的代码块最终会通过如下调用进行计算:

    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.environment() 返回其封闭环境为空环境的环境(即 emptyenv() ). 评估() (就像 评估() 会)寻找符号 <- 在里面 环境 当它在那里找不到它时,就会启动封闭环境链,这里不包含任何匹配项(还记得什么时候 是一个环境,而不是一个列表 参数未使用。)


    推荐解决方案

    要想做你想做的事,你需要创建一个环境:(1)包含你列表中的所有对象,以及(2) 将调用的父环境作为其封闭环境 render() (即调用 渲染() 通常进行评估)。最简洁的方法就是使用俏皮的 list2env()

    env <- list2env(list(y="hello"), parent.frame())
    render('test.Rmd', output_format = "html_document",
            output_file = 'test.html',
            envir = env)
    

    这样做将导致您的代码块由如下代码进行评估,这是您想要的:

    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!"