使用knit()
/ knit2pdf()
代替RStudio中的“编译PDF” 1 按钮会产生哪些(可能不需要的)副作用?
knitr
的大多数用户似乎都在RStudio中编写文档,并使用“编译PDF”/“编织HTML”按钮编译文档。这在大多数情况下都可以顺利运行,但是每隔一段时间就会有使用编译按钮无法实现的特殊要求。在这些情况下,解决方案通常是直接调用knit()
/ knit2pdf()
/ rmarkdown::render()
(或类似函数)。
一些例子:
使用knit2pdf()
代替“编译PDF”按钮通常可以为这些问题提供简单的解决方案。然而,这是有代价的:“编译PDF”处理文档in a separate process and environment的根本区别在于knit2pdf()
而朋友则没有。
这有意义,问题在于并非所有这些含义都是显而易见的。认为knit()
使用来自全球环境的对象(而“编译PDF”没有)举个例子。这可能是显而易见的,并且在上述第二个示例的情况下可能是期望的行为,但当knit()
用于克服示例1和3中的问题时,它是意外后果。
此外,还有更微妙的差异:
每当我读/写建议使用knit2pdf()
而不是“编译PDF”时,我认为“正确,但用户应该理解后果......”。
因此,这里的问题是:
使用
knit()
/knit2pdf()
代替RStudio中的“编译PDF”按钮会产生哪些(可能不需要的)副作用?
如果对此问题有一个全面的(社区维基?)答案,可以将其与未来的答案联系起来,建议使用knit2pdf()
。
这个问题有很多相关的问题。然而,他们要么只提出代码(或多或少)重现RStudio按钮的行为,要么他们解释“基本上”发生了什么,而没有提到可能的陷阱。其他人看起来像是非常相似的问题,但结果却是一个(非常)特殊情况。一些例子:
Sys.sleep(30)
和the "Compile PDF" log都没有洞察力(两个提示都指向同一个事物)。我认为这个问题提出了许多应该成为答案一部分的问题。然而,可能还有更多方面我不知道哪个是我不愿意自己回答这个问题的原因(尽管如果没有人回答我可能会尝试)。
可能答案应该包括三个要点:
knit()
使用来自调用环境的对象(默认值:envir = parent.frame()
)以及对可重现性的影响。我试图解决阻止knit()
在this answer(第二个要点)中使用文档外部对象的问题。我不确定在这个问题上的正确观点。我认为两者都是“当我点击'编译PDF'和含义时会发生什么”以及“当我使用knit()
+含义时会发生什么”是解决问题的好方法。
1 编写RMD文档时,同样适用于“编织HTML”按钮。
答案 0 :(得分:11)
首先,如果您将范围限制为“编译PDF”按钮,我认为这个问题更容易回答,因为“编织HTML”按钮是另一回事。 “编译PDF”仅适用于Rnw文档(R + LaTeX,或者认为是Sweave)。
我会按照你提出的三点回答你的问题:
目前,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()
中)。
由于Rnw文档始终在新的R会话中编译,因此它不会使用当前R会话中的任何对象。这很难仅通过当前R会话中envir
的{{1}}参数进行复制。特别是,您无法使用knitr::knit()
,因为虽然knitr::knit(envir = new.env())
是一个新环境,但它有一个默认的父环境new.env()
,通常是parent.frame()
;您也不能使用globalenv()
,因为它太“干净”,即使在R base 包中也会遇到对象问题。复制“编译PDF”按钮的唯一可靠方法就是我在1中所说的:knitr::knit(envir = emptyenv())
,在这种情况下system2("Rscript", c("-e", shQuote("library(knitr); knit('your.Rnw')"))
使用新的R会话的knit()
。
我不完全确定RStudio对globalenv()
选项的作用。如果未设置,它可能会在幕后自动设置此选项。我认为这是一个相对较小的问题。您可以在repos
中进行设置,我认为RStudio应该尊重您的CRAN镜像设置。
用户一直在问为什么Rnw文档(或R Markdown文档)没有在当前的R会话中编译。对我们来说,它基本上归结为以下哪种后果更令人惊讶或不受欢迎:
总结一下,我想:
在新的R会话中编织对于再现性更好;
在当前的R会话中编织有时更方便(例如,您尝试在当前会话中使用不同的临时R对象编织)。有时你还必须编织当前的R会话,特别是当你以编程方式生成PDF报告时,例如,你使用(for)循环来生成一系列报告。您无法通过“编译PDF”按钮实现此目的(该按钮主要仅用于单个Rnw文档)。
BTW,我认为我上面所说的也适用于Knit或Knit HTML按钮,但基础功能是.Rprofile
而不是rmarkdown::render()
。