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.
RStudio has a great series of articles:
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
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
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.
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.
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.
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.
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.
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)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.
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.
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
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)