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



我有几个Rmd文档,除了标题外,它们都具有相同的YAML前页。我怎么能保持这个frontmatter在一个文件,并有它用于所有的文档?它变得相当大,我不想每次调整frontmatter时都保持每个文件的同步。

I want to still

  • 在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

示例文档

----
title: On the Culinary Preferences of Anthropomorphic Cats
----
I do not like green eggs and ham. I do not like them, Sam I Am!

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

选项/解决方案?

我还没有完全使这些工作。

  • 用标记可以创建一个_output.yaml来放置常见的YAML元数据,但这会将所有的元数据放在YAML的output:下,所以只适用于html_document:pdf_document:下的选项,而不是像作者,日期,…
  • 写一个knitr块来导入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!
    

    如果我knitr('input.Rmd')然后pandoc输出,但如果我使用Rstudio中的Knit按钮(我假设调用render)则不行,因为这在运行knitr之前首先解析元数据,并且元数据是畸形的,直到knitr已经运行。

  • Makefile:如果我足够聪明,我可以写一个Makefile或其他东西来注入common.yamlinput.Rmd,然后运行rmarkdown::render(),并以某种方式将其连接到Rstudio的Knit按钮,也许以某种方式将这个Rstudio配置保存到.Rproj文件中,这样整个事情是可移植的,而不需要我编辑.Rprofile。但是我不够聪明。

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

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

  1. 你可以在命令行上给pandoc提供通用的yaml !分析!
  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
---

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

然后在文档中,你可以指定YAML作为pandoc的最后一个参数,它将应用YAML(参见这个github问题)

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

你甚至可以把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命令

这允许更大的控制,虽然有点麻烦/棘手。

这个链接和这个链接提到了一个未记录的特性:当点击Rstudio的"Knit"按钮时,YAML的knit:部分将被执行。

总之:

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

     knit:  (function (...) { source('myknit.r'); myknit(...) })
    

    它似乎必须在一行上。使用source('myknit.r')而不是仅仅将函数定义放入YAML的原因是为了可移植性。如果我修改myknit.r,我不必修改每个文档的YAML。这样,所有文档必须在其标题中重复的唯一通用 YAML是knit行;所有其他常见的YAML可以留在common.yaml .

然后按Ctrl+Shift+K工作,我希望从Rstudio内。

进一步说明:

  • myknit可能只是一个系统调用make,如果我有一个makefile设置。
  • 注入的YAML将通过rmarkdown传递,因此是针织的,因为它在调用render之前被注入
  • 预览窗口:只要myknit产生(单个)消息Output created: path/to/file.html,那么文件将显示在预览窗口

    我发现在输出中只能有一个这样的消息[不是多个],或者你没有预览窗口。因此,如果您使用render(它使"Output created: basename.extension")消息而最终生成的文件实际上在其他地方,则需要通过render(..., quiet=T)suppressMessages(render(...))(前者也抑制knitr progress和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!

common.yaml[示例]:

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

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)
}

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

@mathematical提出的第一个解。咖啡是一个很好的方法,但是它给出的例子不适合我(可能是因为语法改变了)。如上所述,这可以通过在YAML头文件中提供pandoc参数来实现。例如,

这是header.yaml文件的内容:

title: "Crime and Punishment"
author: "Fyodor Dostoevsky"

添加到RMarkdown文件的开头:

---
output:
  html_document:
    pandoc_args: ["--metadata-file=header.yaml"]
---

参见pandoc手册中的--metadata-file参数

最新更新