Blogging with Jekyll and R Markdown using knitr
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
use knitr
One way to blog using R and Jekyll is to copy and paste every code chunk, output and plot into a plain vanilla markdown file by hand. This is cumbersome, especially for plots which need to be saved as images and embedded. I tried this for my first few posts. It was clunky.
A more seamless solution is to use knitr to convert R Markdown files to Jekyll friendly markdown files. Knitr allows you to write your entire post in R Markdown utilizing all the features of traditional markdown while conveniently running your code, printing your output and displaying your charts. And it looks fresh.
Rmd ==> Jekyll markdown
Knitr is good at transforming R Markdown to HTML, PDF or even Word files. Jekyll is good at turning traditional/Jekyll markdown to HTML (your website). There might be an elegant way to shove the HTML generated by knitr into a jekyll site, but I haven’t found one. I found it much easier to convert R Markdown to its close cousin of markdown that is compatible with Jekyll. This conversion really just boils down to handling the R chunks: saving the plots as SVGs, formatting the code chunks with R syntax highlighting and printing the output for selected chunks.
A convenient feature provided in knitr is the ability to suppress code, messages, warnings, errors and output printed to the console for specific code chunks using the echo
, message
, warning
, error
and eval
parameters respectively.
fuzzy middle
I tried a few approaches, but this one from chepec both worked the best and made the most sense to me. I had to make a couple adjustments to make it work for my setup, but they were small and included below. As I started generating more posts from R Markdown, I found a need to overwrite specific posts (rather than all or none). So I added a parameter overwriteOne
which takes a string input and will overwrite any post that partially matches it (a simple grep
ignoring case). However, my additions are trivial – all the credit goes to chepec.
how to use KnitPost
- Configure the directories at the top of
KnitPost
to match the file system of your blog. I could have included these as parameters, but I figured I wouldn’t re-architect my blog very often… so I left them hard-coded. - Create an R Markdown post and save it as a
.Rmd
file inrmd.path
. Be sure to include the proper YAML front matter for Jekyll. This tripped me up initially. I forgot to change the date from the knitr style date (“Month, day YYYY”) that auto-generates when you create a new .Rmd to a Jekyll style date (‘YYYY-MM-DD’). - Run
KnitPost
to publish your R Markdown file.
KnitPost <- function(site.path='/pathToYourBlog/', overwriteAll=F, overwriteOne=NULL) { if(!'package:knitr' %in% search()) library('knitr') ## Blog-specific directories. This will depend on how you organize your blog. site.path <- site.path # directory of jekyll blog (including trailing slash) rmd.path <- paste0(site.path, "_Rmd") # directory where your Rmd-files reside (relative to base) fig.dir <- "assets/Rfig/" # directory to save figures posts.path <- paste0(site.path, "_posts/articles/") # directory for converted markdown files cache.path <- paste0(site.path, "_cache") # necessary for plots render_jekyll(highlight = "pygments") opts_knit$set(base.url = '/', base.dir = site.path) opts_chunk$set(fig.path=fig.dir, fig.width=8.5, fig.height=5.25, dev='svg', cache=F, warning=F, message=F, cache.path=cache.path, tidy=F) setwd(rmd.path) # setwd to base # some logic to help us avoid overwriting already existing md files files.rmd <- data.frame(rmd = list.files(path = rmd.path, full.names = T, pattern = "\.Rmd$", ignore.case = T, recursive = F), stringsAsFactors=F) files.rmd$corresponding.md.file <- paste0(posts.path, "/", basename(gsub(pattern = "\.Rmd$", replacement = ".md", x = files.rmd$rmd))) files.rmd$corresponding.md.exists <- file.exists(files.rmd$corresponding.md.file) ## determining which posts to overwrite from parameters overwriteOne & overwriteAll files.rmd$md.overwriteAll <- overwriteAll if(is.null(overwriteOne)==F) files.rmd$md.overwriteAll[grep(overwriteOne, files.rmd[,'rmd'], ignore.case=T)] <- T files.rmd$md.render <- F for (i in 1:dim(files.rmd)[1]) { if (files.rmd$corresponding.md.exists[i] == F) { files.rmd$md.render[i] <- T } if ((files.rmd$corresponding.md.exists[i] == T) && (files.rmd$md.overwriteAll[i] == T)) { files.rmd$md.render[i] <- T } } # For each Rmd file, render markdown (contingent on the flags set above) for (i in 1:dim(files.rmd)[1]) { if (files.rmd$md.render[i] == T) { out.file <- knit(as.character(files.rmd$rmd[i]), output = as.character(files.rmd$corresponding.md.file[i]), envir = parent.frame(), quiet = T) message(paste0("KnitPost(): ", basename(files.rmd$rmd[i]))) } } }
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.