Iterate parameterised {xaringan} reports

knitr
pagedown
r
reproducibility
rmarkdown
xaringan
Author
Published

March 12, 2020

The pilot Wedge Antilles from Star Wars saying 'Ha, that got him!'

Driving a Wedge (via Giphy).

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:

rmarkdown::render(
  input = "starwars-template.Rmd", # the template
  params = list(names = "Wedge Antilles")  # different param
)

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
characters <- c("Chewbacca", "Obi-Wan Kenobi", "Wedge Antilles")

# Render to HTML the template for each param
purrr::map(
  .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:

  1. An R Markdown template (with CSS files2 to tweak the default style)
  2. An R script to generate HTML and PDF outputs

The R script basically does three things:

  1. Prepares the dplyr::starwars data set
  2. Uses purrr::map() with the params argument to render a HTML report per character
  3. 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

  1. Because Jedi and Sith are basically space samurai ninjas, no?↩︎

  2. I’ve tried to use Libre Gothic to approximate the Star Wars title crawl font; hopefully this renders correctly for you.↩︎

  3. You’ll need Chrome or Chromium installed to use this function.↩︎

  4. 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).↩︎

Reuse

CC BY-NC-SA 4.0