Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a R markdown analog of \SweaveInput{} for modular report generation?

One of the features I like very much in Sweave is the option to have \SweaveInput{} of separate Sweave files to have a more "modular" report and just be able to comment out parts of the report that I do not want to be generated with a single #\SweaveInput{part_x} rather than having to comment in or out entire blocks of code. Recently I decided to move to R Markdown for multiple reasons being mainly practicality, the option of interactive (Shiny) integration in the report and the fact that I do not really need the extensive formatting options of LaTeX. I found that technically pandoc is able to combine multiple Rmd files into one html output by just concatenating them but it would be nice if this behaviour could be called from a "master" Rmd file.

Any answer would be greatly appreciated even if it is just "go back to Sweave, it is not possible in Markdown".

I am using R 3.1.1 for Windows and Linux as well as Rstudio 0.98.1056 and Rstudio server 0.98.983.

like image 877
FM Kerckhof Avatar asked Jan 07 '15 15:01

FM Kerckhof


People also ask

What is the difference between Markdown and R Markdown?

R Markdown is an extension of the markdown syntax. R Markdown files are plain text files that typically have the file extension . Rmd . They are written using an extension of markdown syntax that enables R code to be embedded in them in a way which can later be executed.

What output formats can be generate from R Markdown?

There are two types of output formats in the rmarkdown package: documents, and presentations.

What is the difference between r/r studio and R Markdown?

To put it simply - R is the actual programming language, RStudio is a convenient interface in which to use it, and R Markdown is a specific type of file format designed to produce documents that include both code and text.

What are the three types of sections in an R Markdown document?

There are three basic components of an R Markdown document: the metadata, text, and code.


2 Answers

Use something like this in the main document:

```{r child="CapsuleRInit.Rmd"}
```
```{r child="CapsuleTitle.Rmd", eval=TRUE}
```
```{r child="CapsuleBaseline.Rmd", eval=TRUE}
```

Use eval=FALSE to skip one child.

For RStudio users: you can define a main document for latex output, but this does not work for RMD documents, so you always have to switch to the main document for processing. Please support my feature request to RStudio; I tried already twice, but is seems to me that too few people use child docs to put it higher in the priority list.

like image 170
Dieter Menne Avatar answered Sep 24 '22 20:09

Dieter Menne


I don't quite understand some of the terms in the answer above, but the solution relates to defining a custom knit: hook in the YAML header. For multipartite documents this allows you to, for example:

  • Have a 'main' or 'root' Rmarkdown file with an output: markdown_document YAML header
  • render all child documents from Rmd ⇒ md ahead of calling render, or not if this is time-limiting
  • combine multiple files (with the child code chunk option) into one (e.g. for chapters in a report)
  • write output: html_document (or other format) YAML headers for this compilation output on the fly, prepending to the markdown effectively writing a fresh Rmarkdown file
    • ...then render this Rmarkdown to get the output, deleting intermediate files in the process if desired

The code for all of the above (dumped here) is described here, a post I wrote after working out the usage of custom knit: YAML header hooks recently (here).

The custom knit: function (i.e. the replacement to rmarkdown::render) in the above example is:

(function(inputFile, encoding) {
  input.dir <- normalizePath(dirname(inputFile))
  rmarkdown::render(input = inputFile, encoding = encoding, quiet=TRUE,
                    output_file = paste0(input.dir,'/Workbook-tmp.Rmd'))
  sink("Workbook-compiled.Rmd")
    cat(readLines(headerConn <- file("Workbook-header.yaml")), sep="\n")
    close(headerConn)
    cat(readLines(rmdConn <- file("Workbook-tmp.Rmd")), sep="\n")
    close(rmdConn)
  sink()

  rmarkdown::render(input = paste0(input.dir,'/Workbook-compiled.Rmd'),
                  encoding = encoding, output_file = paste0(input.dir,'/../Workbook.html'))
  unlink(paste0(input.dir,'/Workbook-tmp.Rmd'))
})

...but all squeezed onto 1 line!

The rest of the 'master'/'root'/'control' file or whatever you want to call it takes care of writing the aforementioned YAML for the final HTML output that goes via an intermediate Rmarkdown file, and its second code chunk programmatically appends child documents through a call to list.files()

```{r include=FALSE}
header.input.file <- "Workbook-header.yaml"
header.input.dir <- normalizePath(dirname(header.input.file))
fileConn <- file(header.input.file)
writeLines(c(
  "---",
  paste0('title: "', rmarkdown::metadata$title,'"'),
  "output:",
  "  html_document:",
  "    toc: true",
  "    toc_depth: 3 # defaults to 3 anyway, but just for ease of modification",
  "    number_sections: TRUE",
  paste0("    css: ",paste0(header.input.dir,'/../resources/workbook_style.css')),
  '    pandoc_args: ["--number-offset=1", "--atx-headers"]',
  "---", sep="\n"),
  fileConn)
close(fileConn)
```

```{r child = list.files(pattern = 'Notes-.*?\\.md')}
# Use file names with strict numeric ordering, e.g. Notes-001-Feb1.md
```

The directory structure would contain a top-level folder with

  1. A final output Workbook.html
  2. A resources subfolder containing workbook_style.css
  3. A documents subfolder containing said main file "Workbook.Rmd" alongside files named as "Notes-001.Rmd", "Notes-002.Rmd" etc. (to ensure a fileglobbing on list.files(pattern = "Notes-.*?\\.Rmd) finds and thus makes them children in the correct order when rendering the main Workbook.Rmd file)

To get proper numbering of files, each constituent "Notes-XXX.Rmd" file should contain the following style YAML header:

---
title: "March: Doing x, y, and z"
knit: (function(inputFile, encoding) { input.dir <- normalizePath(dirname(inputFile)); rmarkdown::render(input = inputFile, encoding = encoding, quiet=TRUE)})
output:
  md_document:
    variant: markdown_github
    pandoc_args: "--atx-headers"
---

```{r echo=FALSE, results='asis', comment=''}
cat("##", rmarkdown::metadata$title)
```

The code chunk at the top of the Rmarkdown document enters the YAML title as a second-level header when evaluated. results='asis' indicates to return plain text-string rather than

[1] "A text string"

You would knit each of these before knitting the main file - it's easier to remove the requirement to render all child documents and just append their pre-produced markdown output.

I've described all of this at the links above, but I thought it'd be bad manners not to leave the actual code with my answer.

I don't know how effective that RStudio feature request website may be... Personally I've not found it hard to look into the source code for these functions, which thankfully are open source, and if there really is something absent rather than undocumented an inner-workings-informed feature request is likely far more actionable by one of their software devs.

I'm not familiar with Sweave, was the above was what you were aiming at? If I understand correctly you just want to control the inclusion of documents in a modular fashion. The child = list.files() statement could take care of that: if not through file globbing you can straight-up list files as child = c("file1.md","file2md")... and switch that statement to change the children. You can also control TRUE/FALSE switches with YAML, whereby the presence of a custom header would set some children to be included for example

potentially.absent.variable: TRUE

...above the document with a silent include=FALSE hiding the machinations of the first chunk:

```{r include=FALSE}
!all(!as.logical(rmarkdown::metadata$potentially.absent.variable)
# ==> FALSE if potentially.absent.variable is absent
# ==> FALSE if anything other than TRUE
# ==> TRUE if TRUE

checkFor <- function(var) {
  return !all(!as.logical(rmarkdown::metadata[[var]])
}
```

```{r child = "Optional_file.md", eval = checkFor(potentially.absent.variable)}
```
like image 35
Louis Maddox Avatar answered Sep 25 '22 20:09

Louis Maddox