This post is about an R Markdown html document format that provides the ability to lazily load plot outputs in R Markdown-generated html.
Since my last post I’ve had my nose to the grindstone on some work for a client and have been (quite enjoyably) a bit of a recluse (even more so than usual) on social media. Interspersed throughout all that work I have been able to work on several interesting things, and the list of things to post about is ever-growing. I’ve finally found some time to get myself back to the blog.
For this post, I want to focus on a small R package I recently wrote to help tackle one of the issues I’ve been dealing with in several of the R Markdown reports and pages I’ve been generating as part of my work.
Problem: Slow-loading R Markdown html documents
I really enjoy being able to embed htmlwidgets in my R Markdown documents (particularly rbokeh plots), but I often get to the point where I either have too many figures on the page or some figures have enough data baggage with them that the page load time becomes unacceptably slow. The slow load time is due to the fact that all of the page’s assets are being loaded up front. One way to get around this is to delay the loading of plots embedded in the page until they are in view or nearly in view for the user, which we refer to as lazy loading.
Solution: lazyrmd
The lazyrmd R package provides an R Markdown html document format that makes it easy to specify htmlwidget outputs (raster images can easily be added - just haven’t been yet) that will be lazily loaded.
Installation
First, you want to install the package. Since this is a new package, I have not put it on CRAN but will once I get some feedback and put it through some more thorough usage.
You can either install from the Tessera DRAT repository:
options(repos = c(tessera = "http://packages.tessera.io",
getOption("repos")))
install.packages("lazyrmd")
or from Github:
devtools::install_github("hafen/lazyrmd")
Using lazyrmd
To enable lazy loading, you need to do two things to your R Markdown document:
Change your output format from
html_document
tolazyhtml_document
in the front-matter of your document, e.g.:--- title: "Lazy Loading Test" author: "Ryan Hafen" output: lazyhtml_document ---
Note that the
lazyhtml_document
is a wrapper aroundhtml_document
so all front-matter options forhtml_document
are valid forlazyhtml_document
.Add
lazy=TRUE
to any chunk whose output is an htmlwidget that you would like to have load lazily, e.g.:```{r, lazy=TRUE} library(rbokeh) figure(width = 800) %>% ly_points(date, Freq, data = flightfreq, hover = list(date, Freq, dow), size = 5) %>% ly_abline(v = as.Date("2001-09-11")) ```
The output of this block will not be part of the page load when viewing the resulting html document. Instead, a blank space for the plot will be held and when the user gets close to the point in the document at which they will be viewing the plot, it will be loaded into the page.
The video at the beginning of this post shows the difference in initial page load time before and after using lazyrmd for the rbokeh documentation page, which was generated with R Markdown using packagedocs.
How it works
Currently the package only works for htmlwidget output, but adding support for lazy loading of raster graphics should be very straightforward.
For htmlwidgets, lazyrmd overrides the knit_print.htmlwidget
method in the htmlwidgets package. If the user has not specified that the output should be lazily loaded with lazy=TRUE
, it passes the plot on to this default method. However, if lazy=TRUE
has been specified, a utility function, print_lazy_widget()
is called which saves out the output of the plot as an independent html file and returns a placeholder for the plot. Javascript code is automatically embedded in the html page that will cause the plot to load based on the page scrolling actions of the user. The recliner.js library with some custom modifications is used behind the scenes.
Notes
- This approach is not possible with a standalone html page output - we must store the plots in separate files. Otherwise, it would defeat the purpose of why this package was created.
- It would be nice if the lazy loading functionality provided in this package could be loaded as a “plugin” instead of as a new R Markdown output format, and that this could somehow be specified in the front-matter of the document. For example, you specify that the document format is
output: html_document
and then somehow specify that you want to use the lazyrmd plugin. Essentially all this package does is inject a few more javascript / css dependencies into the document. The reason a plugin approach would be nice is that I can envision several different html document format plugins that you might want to use together. This may be possible currently and I just need to do some more digging. - Support for raster images really should not be too difficult to add.
Try it out!
If you have run across the same problem with large htmlwidget-heavy R Markdown documents, please give the package a try!