Leading up to the UseR! 2016 conference, I had been looking forward to attending Yihui Xie’s tutorial on R Markdown - there were a few things I had wanted to ask him on how to make custom formats using html_document().

Then, a funny thing happened. A few days before the conference started, I got an email from Yihui, sent also to Karl Broman. Yihui’s visa had been delayed, so he could not attend the conference: could one or both of us deliver his tutorial material?

Given everything Yihui has done for the community, the only possible answer was “yes”. So Karl and I divvied up his slides and each up us did our own deep-dive into our material. As it happened, I was to deliver the material that dealt with my questions - so this was a first-rate opportunity to get it figured out!

In days before the tutorial, with help from Yihui, I got most of it worked out, and in the days and weeks to follow, we sorted out the rest. This page is a distillation of what I had been wanting to figure out.

As I write the rest of this out, I remember fondly the four (somewhat panicked) days of getting everything sorted out with Karl and Yihui. A word of thanks here to Karl for putting together the framework that let us bring tutorial in for a landing. Also, thanks to Yihui for thinking of me as someome not completely unsuitable to fill in. And a huge thanks to the participants in the tutorial for their patience.

References

RStudio has a great series of articles:

HTML document format

Customizing HTML formats

Websites

Yihui’s book.

Get the tutorial slides, etc.

This is a great place to get started on the concepts I try to demonstrate here.

Further: https://github.com/juba/rmdformats

Packages

First, it important to note that this is based on the dev version of R Markdown - pending the next CRAN release.

# devtools::install_github("rstudio/rmarkdown")

To follow along here, you will need to install a package, user2016docdemo, that has a series of functions that return custom formats.

# devtools::install_github("ijlyttle/user2016docdemo")
library("user2016docdemo") 
library("listviewer")
library("magrittr")

Attaching package: 'magrittr'
The following object is masked from 'package:tidyr':

    extract
The following object is masked from 'package:purrr':

    set_names
library("knitr")
Warning: package 'knitr' was built under R version 3.3.2

Examining the format

It will be a lot easier to figure out how customize things if we understand (a little bit) what is going on. Even though R Markdown is capable of so much more, for the purposes of this discussion we will assume that you are interested only in producing an html file.

Format list

The rmarkdown::render() function has two jobs: (1) to knit the R Markdown file into a Markdown file, (2) to use pandoc to turn the Markdown file into an html file. In order to do this, render() needs a set of instructions - this set of instructions is a format, and it is stored as a list.

The purpose of the rmarkdown::html_document() function is to help you to generate this list. Using Kent Russell’s listviewer package, let’s look at the default format returned by html_document() function.

rmarkdown::html_document() %>%
  jsonedit(mode = "view")

For our purposes, we are most-interested in the first parts of the list, entitled “knitr” and “pandoc”. You may wish to click around to see what’s what.

Format function

The job of any R Markdown format function, including your custom format functions, is to return the format list.

rmarkdown::html_document %>%
  formals() %>%
  jsonedit(mode = "view")

These should look familiar, as they are these are parameters that you use to put in a yaml header at the start of an R Markdown document. For example:

title: "A Tale of Two Cities"
output: 
  html_document:
    toc: true
    toc_float: true

When rmarkdown::render() is run (either from the knit button, or from the command line), it passes along the yaml information “under” html_document: to the rmarkdown::html_document() function. It bears repeating that the best way to figure out how to construct your yaml header is to look at the help page for html_document().

As you design your custom formats, keep in mind that you will be able to pass arguments to your format function using the yaml header.

Custom formats

The biggest biggest mental-block (as least for me) to making an easily deployable custom-format is building a package. The reason this is necessary is that when we hit the knit button in RStudio, the render() function runs in a new R session - this is for a very good reason. The only way I know to get the render() function the “list of instructions” is to provide a package function that returns it.

Package

Luckily, Hadley has made it a whole lot easier for all of us to develop packages. I’d highly recommend his book on building packages, available online: http://r-pkgs.had.co.nz/.

Here’s a minimal list of steps.

  • open an empty project
  • devtools::create(".")
  • devtools::use_package("rmarkdown")
  • devtools::use_readme_rmd()
  • devtools::use_mit_license() (if that’s your thing)
  • use_github() (assumes you have a github.com account)
  • write and save some custom functions (rest of this section)
  • hit the build button to install locally
  • commit and push to GitHub every so often

Simplest possible (do nothing)

The simplest possible custom format is a a function that simply passes everything on to rmarkdown::html_document(). Here’s the source code for the simplest-possible custom-format function, one that simply passes everything along to rmarkdown::html_document()

html_doc_0 <-
function(...){

  rmarkdown::html_document(...)

}

When we look at the list of instructions returned by the default, we see that it is (hopefully) no different than before.

user2016docdemo::html_doc_0() %>%
  jsonedit(mode = "view")

At some point, I’d like to put some screenshots here of a sample page - or maybe just link to the results.

Change some defaults

One simple thing that you can do is to change some of the defaults of the html_document() format:

html_doc_1 <-
function(code_folding = c("show", "hide", "none"),
                       theme = "readable", ...){

  code_folding <- match.arg(code_folding)

  rmarkdown::html_document(
    code_folding = code_folding,
    theme = theme,
    ...
  )

}

Similary, we can look at what the function returns:

user2016docdemo::html_doc_1() %>%
  jsonedit(mode = "view")

It is not immedately apparent that there is a difference between the output of html_doc_0() and html_doc_1(). Being both stubborn and not-so-bright, I spent way too much time tracking down where we can find the difference. Here it is: the format list has some elements that are functions; functions have environments. Therein lies the difference. Let’s look at the environment of one of the functions returned as a part of html_doc_1():

html_doc_1() %>%
  `[[`("pre_processor") %>%
  environment() %>%
  `[[`("overlay") %>%
  environment() %>%
  as.list() %>%
  jsonedit(mode = "view")

We see our information in the args element, code_folding has a value of "show" and theme has a value of "readable".

It is probably more useful to demonstrate what this does, rather than show the details of environments, so put a link here eventually.

Make your own theme

Here’s where we can have some fun. If the standard themes are not what you need (or want), you can create your own bootstrap theme at http://bootstrap-live-customizer.com/.

This is a re

Add your own knitr options

Session info

devtools::session_info()
Session info ----------------------------------------------------------------------------
 setting  value                       
 version  R version 3.3.1 (2016-06-21)
 system   x86_64, darwin13.4.0        
 ui       RStudio (1.0.44)            
 language (EN)                        
 collate  en_US.UTF-8                 
 tz       America/Chicago             
 date     2016-12-07                  
Packages --------------------------------------------------------------------------------
 package         * version     date       source                            
 assertthat        0.1         2013-12-06 CRAN (R 3.3.0)                    
 backports         1.0.4       2016-10-24 cran (@1.0.4)                     
 broom           * 0.4.1       2016-06-24 cran (@0.4.1)                     
 codetools         0.2-14      2015-07-15 CRAN (R 3.3.1)                    
 colorspace        1.2-6       2015-03-11 CRAN (R 3.3.0)                    
 DBI               0.5-1       2016-09-10 CRAN (R 3.3.0)                    
 devtools        * 1.12.0.9000 2016-11-21 Github (hadley/devtools@2e3c4b6)  
 digest            0.6.10      2016-08-02 cran (@0.6.10)                    
 dplyr           * 0.5.0       2016-06-24 cran (@0.5.0)                     
 evaluate          0.10        2016-10-11 cran (@0.10)                      
 foreign           0.8-66      2015-08-19 CRAN (R 3.3.1)                    
 ggplot2         * 2.1.0       2016-03-01 CRAN (R 3.3.0)                    
 gtable            0.2.0       2016-02-26 CRAN (R 3.3.0)                    
 htmlDocumentIJL   0.0.0.9000  2016-09-04 local                             
 htmltools         0.3.5       2016-03-21 CRAN (R 3.3.0)                    
 htmlwidgets       0.6         2016-02-25 CRAN (R 3.3.0)                    
 jsonlite          1.1         2016-09-14 CRAN (R 3.3.0)                    
 knitr           * 1.15.1      2016-11-22 cran (@1.15.1)                    
 labeling          0.3         2014-08-23 CRAN (R 3.3.0)                    
 lattice           0.20-33     2015-07-14 CRAN (R 3.3.1)                    
 lazyeval          0.2.0.9000  2016-09-22 Github (hadley/lazyeval@c155c3d)  
 listviewer      * 1.0         2016-06-15 CRAN (R 3.3.0)                    
 magrittr        * 1.5         2014-11-22 CRAN (R 3.3.0)                    
 memoise           1.0.0       2016-01-29 CRAN (R 3.3.0)                    
 mnormt            1.5-4       2016-03-09 cran (@1.5-4)                     
 modelr          * 0.1.0       2016-08-31 CRAN (R 3.3.0)                    
 munsell           0.4.3       2016-02-13 CRAN (R 3.3.0)                    
 nlme              3.1-128     2016-05-10 CRAN (R 3.3.1)                    
 pkgbuild          0.0.0.9000  2016-11-21 Github (r-pkgs/pkgbuild@65eace0)  
 pkgload           0.0.0.9000  2016-11-21 Github (r-pkgs/pkgload@def2b10)   
 plyr              1.8.4       2016-06-08 cran (@1.8.4)                     
 pryr            * 0.1.2       2015-06-20 CRAN (R 3.3.0)                    
 psych             1.6.9       2016-09-17 CRAN (R 3.3.0)                    
 purrr           * 0.2.2.9000  2016-11-21 Github (hadley/purrr@5360143)     
 R6                2.2.0       2016-10-05 cran (@2.2.0)                     
 Rcpp              0.12.8      2016-11-17 cran (@0.12.8)                    
 reshape2          1.4.2       2016-10-22 CRAN (R 3.3.1)                    
 rmarkdown         1.2.9000    2016-12-01 Github (rstudio/rmarkdown@de08391)
 rprojroot         1.1         2016-10-29 cran (@1.1)                       
 rsconnect         0.5         2016-10-17 CRAN (R 3.3.1)                    
 scales            0.4.0       2016-02-26 CRAN (R 3.3.0)                    
 stringi           1.1.2       2016-10-01 CRAN (R 3.3.0)                    
 stringr           1.1.0       2016-08-19 CRAN (R 3.3.0)                    
 tibble          * 1.2         2016-08-26 CRAN (R 3.3.0)                    
 tidyr           * 0.6.0.9000  2016-09-07 Github (hadley/tidyr@3c9335b)     
 user2016docdemo * 0.0.0.9000  2016-09-06 local                             
 utilrSE         * 0.1.99      2016-11-15 local                             
 withr             1.0.2       2016-06-20 CRAN (R 3.3.0)                    
 yaml              2.1.14      2016-11-12 cran (@2.1.14)