在rstudio/knitr文档中导入常见的YAML

mat*_*fee 9 r knitr r-markdown

我有几个Rmd文档,除了标题之外都有相同的YAML前端.如何将此前置文件保存在一个文件中并将其用于所有文档?它变得相当大,我不希望每次调整前端时都保持每个文件的步骤.

我还想

  • 使用RStudio中的Knit按钮/ Ctrl+ Shift+ K快捷键进行编译
  • 保持整个设置的可移植性:希望避免编写自定义输出格式或覆盖rstudio.markdownToHTML(因为这需要我随身携带.Rprofile)

common.yaml:

author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
# many other options
Run Code Online (Sandbox Code Playgroud)

一个示例文档

----
title: On the Culinary Preferences of Anthropomorphic Cats
----

I do not like green eggs and ham. I do not like them, Sam I Am!
Run Code Online (Sandbox Code Playgroud)

期望的输出:已编译的示例文档(即HTML或PDF),已使用common.yaml注入的元数据进行编译.YAML中的R代码(在本例中为日期)将被编译为奖励,但它是没必要(我只把它用于我不需要的日期).

选项/解决方案?

我还没有完成任何这些工作.

  • 使用rmarkdown可以创建一个_output.yaml放置常见的YAML元数据,但这会将所有元数据放在output:YAML中,因此只适用于html_document:和下的选项pdf_document:,而不是像作者,日期等...
  • 写一个knitr chunk来导入YAML,例如

    ----
    title: On the Culinary Preferences of Anthropomorphic Cats
    ```{r echo=F, results='asis'}
    cat(readLines('common.yaml'), sep='\n')
    ```
    ----
    
    I do not like green eggs and ham. I do not like them, Sam I Am!
    
    Run Code Online (Sandbox Code Playgroud)

    如果我knitr('input.Rmd')然后pandoc输出,这是有效的,但如果我使用Rstudio中的Knit按钮(我假设调用render),则不行,因为这会在运行knitr之前先解析元数据,并且元数据格式错误,直到knitr运行.

  • Makefile文件:如果我足够聪明,我可以写一个Makefile文件或东西注射common.yamlinput.Rmd,然后运行rmarkdown::render(),并以某种方式把它挂到Rstudio的针织按钮,也许这Rstudio配置以某种方式保存到.Rproj文件中,这样整个事情是便携式没有我需要编辑.Rprofile.但我不够聪明.

编辑:我最后一个选项,并将Makefile连接到Build命令(Ctrl+ Shift+ B).但是,每次我通过Ctrl+ Shift+ 使用它时都会构建相同的目标B,并且我想构建与目前在编辑器中打开的Rmd文件相对应的目标[和Ctrl+ Shift+ K].

mat*_*fee 8

找到了两个可移植的选项(即不需要.Rprofile定制,YAML前端的最小重复):

  1. 您可以在命令行上为pandoc提供常见的yaml!D'哦!
  2. 您可以将knit:元数据的属性设置为您自己的函数,以便更好地控制Ctrl + Shift + K时发生的情况.

选项1:命令行的常见YAML.

将所有常见的YAML放在自己的文件中

common.yaml:

---
author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
---
Run Code Online (Sandbox Code Playgroud)

注意它是完整的,即---需要的.

然后在文档中,您可以将YAML指定为pandoc的最后一个参数,并且它将应用YAML(请参阅此github问题)

example.rmd:

---
title: On the Culinary Preferences of Anthropomorphic Cats
output:
  html_document:
    pandoc_args: './common.yaml'
---

I do not like green eggs and ham. I do not like them, Sam I Am!
Run Code Online (Sandbox Code Playgroud)

你甚至可以把html_document:东西在_output.yaml,因为rmarkdown将采取,并将其放置在output:该文件夹中的所有文档.通过这种方式,使用此前端的所有文档之间不会出现YAML重复.

优点:

  • 没有重复的YAML前线.
  • 很干净

缺点:

  • 公共YAML未通过knit,因此不会解析上面的日期字段.您将获得文字字符串"r format(Sys.time(),format ='%Y-%m-%d%H:%M:%S%z')"作为您的日期.
  • 来自同一个github问题:

    首先看到的元数据定义保持不变,即使稍后解析了冲突的数据.

根据您的设置,这可能会在某些时候出现问题.

选项2:覆盖knit命令

这允许更大的控制,但有点麻烦/棘手.

这个链接这个链接在rmarkdown中提到了一个未记录的功能:knit:当单击Rstudio的"Knit"按钮时,将执行YAML 的一部分.

简而言之:

  1. 定义一个函数myknit(inputFile, encoding),它将读取YAML,将其放入RMD并调用render结果.保存在自己的文件中myknit.r.
  2. 在YAML中example.rmd,添加

     knit:  (function (...) { source('myknit.r'); myknit(...) })
    
    Run Code Online (Sandbox Code Playgroud)

    它似乎必须在一条线上.这样做的原因source('myknit.r'),而不是仅仅把函数的定义诠释他YAML是便携性.如果我修改myknit.r我不必修改每个文件的YAML.这样,所有文档必须在其前端重复的唯一常见 YAML是knit行; 所有其他常见的YAML都可以留下来common.yaml.

然后Ctrl + Shift + K就像我希望在Rstudio中一样.

附加说明:

  • myknitmake如果我有一个makefile设置,可能只是一个系统调用.
  • 注入的YAML将通过rmarkdown并因此编织,因为它是调用之前注入的render.
  • 预览窗口:只要myknit生成(单个)消息Output created: path/to/file.html,该文件就会显示在预览窗口中.

    我发现输出中只有一个这样的消息[不是多个],或者你没有预览窗口.因此,如果您使用render(生成"输出已创建:basename.extension")消息并且最终生成的文件实际上在其他地方,则需要通过render(..., quiet=T)或者suppressMessages(render(...))(前者抑制knitr进度和pandoc输出)来抑制此消息,并且使用正确的路径创建自己的消息.

优点:

  • YAML前线是针织的
  • 如果您需要进行自定义的预处理/后处理,则比选项1更多的控制.

缺点:

  • 比选项1更省力
  • knit:行必须在每个文档中重复(尽管source('./myknit.r')至少函数定义可以存储在一个中心位置)

这是后人的设置.对于便携性,你只需要随身携带myknit.rcommon.yaml.不需要.Rprofile或特定于项目的配置.

example.rmd:

---
title: On the Culinary Preferences of Anthropomorphic Cats
knit:  (function (...) { source('myknit.r'); myknit(...) })
---

I do not like green eggs and ham. I do not like them, Sam I Am!
Run Code Online (Sandbox Code Playgroud)

common.yaml [例如]:

author: me
date: "`r format (Sys.time(), format='%Y-%m-%d %H:%M:%S %z')`"
link-citations: true
reference-section-title: References
Run Code Online (Sandbox Code Playgroud)

myknit.r:

myknit <- function (inputFile, encoding, yaml='common.yaml') {   
    # read in the YAML + src file
    yaml <- readLines(yaml)
    rmd <- readLines(inputFile)

    # insert the YAML in after the first ---
    # I'm assuming all my RMDs have properly-formed YAML and that the first
    # occurence of --- starts the YAML. You could do proper validation if you wanted.
    yamlHeader <- grep('^---$', rmd)[1]
    # put the yaml in
    rmd <- append(rmd, yaml, after=yamlHeader)

    # write out to a temp file
    ofile <- file.path(tempdir(), basename(inputFile))
    writeLines(rmd, ofile)

    # render with rmarkdown.
    message(ofile)
    ofile <- rmarkdown::render(ofile, encoding=encoding, envir=new.env())

    # copy back to the current directory.
    file.copy(ofile, file.path(dirname(inputFile), basename(ofile)), overwrite=T)
}
Run Code Online (Sandbox Code Playgroud)

从编辑器按Ctrl + Shift + K/Knit example.rmd将编译结果并显示预览.我知道它正在使用common.yaml,因为结果包括日期和作者,而example.rmd它本身没有日期或作者.