::render(
rmarkdowninput = "starwars-template.Rmd", # the template
params = list(names = "Wedge Antilles") # different param
)
tl;dr
You want to use R to generate multiple reports from a single template, each containing different data.
How? Create a parameterised R Markdown template with a params
YAML argument. Iterate over param values with rmarkdown::render()
inside purrr::map()
.
I made a demo of this approach that focuses on parameterised {xaringan} slides. It includes a further {purrr} step with pagedown::chrome_print()
to render the HTML outputs to PDF.
Parambulate
R Markdown lets you integrate code into a document, which is great for automating the production of reproducible reports.
Parameterised R Markdown reports let you control the content of your output by providing a variable to the document at rendering time. You can create multiple reports with different data, but the same template.
How does this work? You provide a special params
argument to the YAML header of your R Markdown document. Let’s say we have a template that renders a report about Star Wars characters1: starwars-template.Rmd
. We might use a name
param to declare a character name:
---
title: Star Wars
author: Matt Dray
date: 2020-03-12
params:
name: "Obi-Wan Kenobi"
---
Now "Obi-Wan Kenobi"
will be supplied wherever you reference params$name
in the code of your document.
Maybe you’re filtering the dplyr::starwars
data set to get eye color, so filter(starwars, name == params$name) %>% pull(eye_color)
will return blue-gray
when rendered.
Change the param to name: Chewbacca
and every instance of params$name
will take the new value on render. Our call to get eye color will now return blue
.
Automate
How can you automate the process of opening the document and changing the parameter value by hand?
You can supply a different value via the params
argument of render()
from the {rmarkdown} package:
And if you have multiple values to supply? You can iterate with the map()
function from {purrr} to supply several parameter values in turn, resulting in a separate output for each one.
# Create a vector of the elements to iterate over
<- c("Chewbacca", "Obi-Wan Kenobi", "Wedge Antilles")
characters
# Render to HTML the template for each param
::map(
purrr.x = characters, # vector of param values
.f = ~render(
input = "starwars-template.Rmd", # R Markdown filepath
params = list(name = .x), # iterated parameter value
output_file = paste0(.x, ".html") # iterated output path
)
))
Note that you can have parameterised reports with more than one param and can provide various combinations to render()
. Use map2()
or pmap()
from {purrr} to iterate with multiple params.
Don’t forget you can also use the {furrr} package’s future_map()
to speed up the process, since it takes advantage of parallel processing.
Demo: Ninja Knitting
I’ve created a demo on GitHub that extends the ideas above to a {xaringan} slide template to produce ‘micro-dossiers’ on some Star Wars characters. It uses iterative rendering, but also has another iterative step to convert the HTML outputs to PDF format.
There are two main files in the demo:
- An R Markdown template (with CSS files2 to tweak the default style)
- An R script to generate HTML and PDF outputs
The R script basically does three things:
- Prepares the
dplyr::starwars
data set - Uses
purrr::map()
with theparams
argument to render a HTML report per character - Uses
pagedown::chrome_print()
to render each HTML document to PDF
chrome_print()
is a handy function that uses the Chrome browser’s ability to print from HTML to PDF, but without actually opening Chrome3.
You can find all the HTML files and PDF files from the GitHub repo.4 Here’s an example that uses the param name: "Obi-Wan Kenobi"
:
And here’s another, this time with the param set to name: "Wedge Antilles"
:
I think it was Yoda who said something like:
R Markdown is the path to automated {xaringan} PDF production. R Markdown leads to parameterised reports. Parameterised reports lead to multiple HTMLs. Multiple HTMLs leads to multiple PDFs.
So wise.
Environment
Session info
Last rendered: 2023-07-22 16:06:08 BST
R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.2.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.11.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/London
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] htmlwidgets_1.6.2 compiler_4.3.1 fastmap_1.1.1
[4] cli_3.6.1 tools_4.3.1 htmltools_0.5.5
[7] xaringanExtra_0.7.0 rstudioapi_0.15.0 yaml_2.3.7
[10] rmarkdown_2.23 knitr_1.43.1 jsonlite_1.8.7
[13] xfun_0.39 digest_0.6.33 rlang_1.1.1
[16] evaluate_0.21
Footnotes
Because Jedi and Sith are basically space
samuraininjas, no?↩︎I’ve tried to use Libre Gothic to approximate the Star Wars title crawl font; hopefully this renders correctly for you.↩︎
You’ll need Chrome or Chromium installed to use this function.↩︎
You can also view each of the HTML files online in the form https://matt-dray.github.io/ninja-knitting/obiwankenobi.html (change ‘obiwankenobi.html’ to ‘chewbacca.html’ for example).↩︎