区别:RStudio 中的 "Compile PDF" 按钮与 knit() 和 knit2pdf()

Difference: "Compile PDF" button in RStudio vs. knit() and knit2pdf()

TL;DR

在 RStudio 中使用 knit()/knit2pdf() 而不是“编译 PDF”1 按钮有什么(可能不需要的)副作用?

动机

knitr 的大多数用户似乎在 RStudio 中编写文档并使用“编译 PDF”/“编织 HTML”按钮编译文档。这在大多数情况下都能顺利进行,但偶尔也会出现使用编译按钮无法实现的特殊要求。在这些情况下,解决方案通常是直接调用 knit()/knit2pdf()/rmarkdown::render()(或类似函数)。

一些例子:

使用 knit2pdf() 而不是“编译 PDF”按钮通常可以为此类问题提供简单的解决方案。然而,这是有代价的:“编译 PDF”处理文档 in a separate process and environmentknit2pdf() 和朋友不处理文档存在根本区别。

这有影响,问题是并非所有这些影响都是显而易见的。考虑到 knit() 使用全局环境中的对象这一事实(而“编译PDF" 没有)作为示例。这可能是显而易见的,并且在上面第二个示例的情况下是期望的行为,但是当 knit() 用于克服示例 1 和 3 中的问题时,这是 意外结果

此外,还有更细微的差别:

问题及其目标

每当我 read/write 建议使用 knit2pdf() 而不是“编译 PDF”时,我认为 ” 是正确的,但用户应该理解后果......”.

因此,这里的问题是:

What are the (possibly unwanted) side-effects of using knit()/knit2pdf() instead of the "Compile PDF" button in RStudio?

如果这个问题有全面的(社区 wiki?)答案,可以在以后的答案中链接,建议使用 knit2pdf()

相关问题

与此相关的问题有几十个。但是,他们要么只建议(或多或少)重现 RStudio 按钮行为的代码,要么解释“基本上”发生了什么,而没有提及可能的陷阱。其他人看起来像是非常相似的问题,但结果却是它的(非常)特例。一些例子:

关于答案

我认为这个问题提出了许多应该成为答案一部分的问题。但是,可能还有更多方面我不知道,这就是我不愿意自己回答这个问题的原因(尽管如果没有人回答我可能会尝试)。

大概,一个答案应该包括三个要点:

我不确定对这个问题的正确看法。我认为“当我点击 'Compile PDF' + implications 时会发生什么”以及“当我使用 knit() + implications 时会发生什么”都是解决这个问题的好方法。


1写RMD文件时的“KnitHTML”按钮同理

首先,我认为如果将范围限制在 "Compile PDF" 按钮,我认为这个问题更容易回答,因为 "Knit HTML" 按钮是另一回事。 "Compile PDF" 仅适用于 Rnw 文档(R + LaTeX,或想想 Sweave)。

我会根据您建议的三点来回答您的问题:

  1. 目前RStudio总是启动一个新的R会话来编译Rnw文件,并首先将工作目录更改为Rnw文件所在的目录。你可以把这个过程想象成一个像这样的 shell 脚本:

    cd path/to/your-Rnw-directory
    Rscript -e "library(knitr); knit('your.Rnw')"
    pdflatex your.tex
    

    请注意,knitr 包总是附加的,pdflatex 可能是其他 LaTeX 引擎(取决于 Sweave 文档的 RStudio 配置,例如 xelatex).如果您想在当前的 R 会话中复制它,您可以在 R:

    中重写脚本
    owd = setwd("path/to/your-Rnw-directory")
    system2("Rscript", c("-e", shQuote("library(knitr); knit('your.Rnw')"))
    system2("pdflatex", "your.tex")
    setwd(owd)
    

    这不像knitr::knit('path/to/your.Rnw')那么简单,在这种情况下,工作目录不会自动更改,所有内容都在当前 R 会话中执行(默认情况下在 globalenv() 中)。

  2. 因为 Rnw 文档总是在新的 R 会话中编译,所以它不会使用当前 R 会话中的任何对象。这很难仅通过当前 R 会话中 knitr::knit()envir 参数来复制。特别是,您不能使用 knitr::knit(envir = new.env()),因为尽管 new.env() 是一个新环境,但它有一个默认的父环境 parent.frame(),通常是 globalenv();您也不能使用 knitr::knit(envir = emptyenv()),因为它是 "too clean",即使在 R base 包中,您也会遇到对象问题。复制 "Compile PDF" 按钮功能的唯一可靠方法是我在 1 中所说的:system2("Rscript", c("-e", shQuote("library(knitr); knit('your.Rnw')")),在这种情况下 knit() 使用新 R 会话的 globalenv()

  3. 我不完全确定 RStudio 对 repos 选项做了什么。如果未设置,它可能会在幕后自动设置此选项。我认为这是一个相对较小的问题。您可以在 .Rprofile 中设置它,我认为 RStudio 应该尊重您的 CRAN 镜像设置。

用户一直在问为什么Rnw文档(或R Markdown文档)没有在当前R会话中编译。对我们来说,它基本上归结为以下哪个后果更令人惊讶或更不受欢迎:

  1. 如果我们在当前 R 会话中编织文档,则无法保证您的结果可以在另一个 R 会话中重现(例如,下次您打开 RStudio 时,或者您的合作者在他们的计算机上打开 RStudio 时)。
  2. 如果我们在新的 R 会话中编写文档,用户可能会惊讶于找不到对象(当他们在 R 控制台中键入对象名称时,他们可以看到它们)。这可能会令人惊讶,但它也是一个很好的早期提醒,提醒您您的文档下次可能无法使用。

总结一下,我认为:

  • 在新的 R 会话中编织更利于重现;

  • 在当前 R 会话中编织有时更方便(例如,您尝试在当前会话中使用不同的临时 R 对象进行编织)。有时您还必须在当前 R 会话中编织,尤其是当您以编程方式生成 PDF 报告时,例如,您使用 (for) 循环生成一系列报告。您无法仅通过 "Compile PDF" 按钮实现此目的(该按钮主要仅用于单个 Rnw 文档)。

顺便说一句,我认为我上面所说的也适用于 Knit 或 Knit HTML 按钮,但底层功能是 rmarkdown::render() 而不是 knitr::knit()