在rstudio/knitr文档中导入常见的YAML [英] Importing common YAML in rstudio/knitr document

查看:77
本文介绍了在rstudio/knitr文档中导入常见的YAML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些Rmd文档,除了标题外,它们都具有相同的YAML主题. 如何将这一前题保存在一个文件中并用于所有文档?它变得越来越大,我不想每次调整前题时都保持每个文件同步. /p>

我想静止

  • 使用RStudio中的编织"按钮/ 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代码(在这种情况下,日期)将作为奖励被编译,但是它是不必要(我只在不需要的日期使用它.)

选项/解决方案?

这些功能我还没有得到解决.

  • 使用rmarkdown可以创建一个_output.yaml来放置常见的YAML元数据,但这会将所有这些元数据放在YAML中的output:下,因此仅适用于html_document:pdf_document:下的选项,而不适用于对于诸如作者,日期等...
  • 编写一个编织块以导入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中的编织"按钮(我假设调用了render)则不起作用,因为这会在运行knitr之前先解析元数据,并且元数据的格式不正确,直到运行knitr为止.

  • Makefile:如果我足够聪明,我可以编写一个Makefile或将common.yaml注入到input.Rmd的东西,然后运行rmarkdown::render(),并以某种方式将其连接到Rstudio的编织"按钮,也许可以以某种方式保存它将Rstudio配置配置到.Rproj文件中,以便整个内容都可移植,而我也无需编辑.Rprofile.但是我还不够聪明.

编辑:我在最后一个选项上使用了一个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问题:

    首先看到的元数据定义将保留并保持不变,即使稍后解析冲突的数据也是如此.

  • 在某些时候,也许这可能是个问题,具体取决于您的设置.

    选项2:覆盖knit命令

    这虽然可以增加一些麻烦/棘手的功能,但却可以提供更大的控制权.

    此链接提到了rmarkdown中未记录的功能:当单击Rstudio的编织"按钮时,将执行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.这样,所有文档必须在其开头重复的唯一 common YAML是knit行.所有其他常见的YAML都可以保留在common.yaml中.

    然后Ctrl + Shift + K可以像我期望的那样在Rstudio中使用.

    更多说明:

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

      我发现输出中只能有一个这样的消息[不是多个],或者您没有预览窗口.因此,如果您使用render(这会生成已创建输出:basename.extension")消息,而最终生成的文件实际上在其他位置,则您需要通过render(..., quiet=T)suppressMessages(render(...))禁止显示此消息(前者禁止显示编织进度和pandoc输出),并使用正确的路径创建您的 own 消息.

    优点:

    • 编织了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/编织将编译结果并显示预览.我知道它正在使用common.yaml,因为结果包括日期和作者,而example.rmd本身没有日期或作者.

    I have a few Rmd documents that all have the same YAML frontmatter except for the title. How can I keep this frontmatter in one file and have it used for all the documents? It is getting rather large and I don't want to keep every file in step every time I tweak the frontmatter.

    I want to still

    • use the Knit button/Ctrl+Shift+K shortcut in RStudio to do the compile
    • keep the whole setup portable: would like to avoid writing a custom output format or overriding rstudio.markdownToHTML (as this would require me to carry around a .Rprofile too)

    Example

    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
    

    an example document

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

    Desired output: The compiled example document (ie either HTML or PDF), which has been compiled with the metadata in common.yaml injected in. The R code in the YAML (in this case, the date) would be compiled as a bonus, but it is not necessary (I only use it for the date which I don't really need).

    Options/Solutions?

    I haven't quite got any of these working yet.

    • With rmarkdown one can create a _output.yaml to put common YAML metadata, but this will put all of that metadata under output: in the YAML so is only good for options under html_document: and pdf_document:, and not for things like author, date, ...
    • write a knitr chunk to import the YAML, e.g.

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

      This works if I knitr('input.Rmd') and then pandoc the output, but not if I use the Knit button from Rstudio (which I assume calls render), because this parses the metadata first before running knitr, and the metadata is malformed until knitr has been run.

    • Makefile: if I was clever enough I could write a Makefile or something to inject common.yaml into input.Rmd, then run rmarkdown::render(), and somehow hook it up to the Knit button of Rstudio, and perhaps somehow save this Rstudio configuration into the .Rproj file so that the whole thing is portable without me needing to edit .Rprofile too. But I'm not clever enough.

    EDIT: I had a go at this last option and hooked up a Makefile to the Build command (Ctrl+Shift+B). However, this will build the same target every time I use it via Ctrl+Shift+B, and I want to build the target that corresponds with the Rmd file I currently have open in the editor [as for Ctrl+Shift+K].

    解决方案

    Have found two options to do this portably (ie no .Rprofile customisation needed, minimal duplication of YAML frontmatter):

    1. You can provide common yaml to pandoc on the command-line! d'oh!
    2. You can set the knit: property of the metadata to your own function to have greater control over what happens when you Ctrl+Shift+K.

    Option 1: common YAML to command line.

    Put all the common YAML in its own file

    common.yaml:

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

    Note it's complete, ie the --- are needed.

    Then in the document you can specify the YAML as the last argument to pandoc, and it'll apply the YAML (see this github issue)

    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!
    

    You could even put the html_document: stuff in an _output.yaml since rmarkdown will take that and place it under output: for all the documents in that folder. In this way there can be no duplication of YAML between all documents using this frontmatter.

    Pros:

    • no duplication of YAML frontmatter.
    • very clean

    Cons:

    • the common YAML is not passed through knit, so the date field above will not be parsed. You will get the literal string "r format(Sys.time(), format='%Y-%m-%d %H:%M:%S %z')" as your date.
    • from the same github issue:

      Metadata definitions seen first are kept and left unchanged, even if conflicting data is parsed at a later point.

    Perhaps this could be a problem at some point depending on your setup.

    Option 2: override the knit command

    This allows for much greater control, though is a bit more cumbersome/tricky.

    This link and this one mention an undocumented feature in rmarkdown: the knit: part of the YAML will be executed when one clicks the "Knit" button of Rstudio.

    In short:

    1. define a function myknit(inputFile, encoding) that would read the YAML, put it in to the RMD and call render on the result. Saved in its own file myknit.r.
    2. in the YAML of example.rmd, add

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

      It seems to have to be on one line. The reason for source('myknit.r') instead of just putting the function definition int he YAML is for portability. If I modify myknit.r I don't have to modify every document's YAML. This way, the only common YAML that all documents must repeat in their frontmatter is the knit line; all other common YAML can stay in common.yaml.

    Then Ctrl+Shift+K works as I would hope from within Rstudio.

    Further notes:

    • myknit could just be a system call to make if I had a makefile setup.
    • the injected YAML will be passed through rmarkdown and hence knitted, since it is injected before the call to render.
    • Preview window: so long as myknit produces a (single) message Output created: path/to/file.html, then the file will be shown in the preview window.

      I have found that there can be only one such message in the output [not multiple], or you get no preview window. So if you use render (which makes an "Output created: basename.extension") message and the final produced file is actually elsewhere, you will need to suppress this message via either render(..., quiet=T) or suppressMessages(render(...)) (the former suppresses knitr progress and pandoc output too), and create your own message with the correct path.

    Pros:

    • the YAML frontmatter is knitted
    • much more control than option 1 if you need to do custom pre- / post-processing.

    Cons:

    • a bit more effort than option 1
    • the knit: line must be duplicated in each document (though by source('./myknit.r') at least the function definition may be stored in one central location)

    Here is the setup for posterity. For portability, you only need to carry around myknit.r and common.yaml. No .Rprofile or project-specific config needed.

    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 [for example]:

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

    Pressing Ctrl+Shift+K/Knit from the editor of example.rmd will compile the result and show a preview. I know it is using common.yaml, because the result includes the date and author whereas example.rmd on its own does not have a date or author.

    这篇关于在rstudio/knitr文档中导入常见的YAML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆