Recently at work, I’ve been building R Markdown documents to generate PowerPoint slides. In general, this is a pretty straightforward process and is described in R Markdown: The Definitive Guide. One limitation they note is:
Images and tables will always be placed on new slides. The only elements that can coexist with an image or table on a slide are the slide header and image/table caption. When you have a text paragraph and an image on the same slide, the image will be moved to a new slide automatically.
That is a bummer for me because the original PowerPoint slides I am trying to replicate using R Markdown have multiple images and tables on them. An alternative to using PowerPoint would be xaringan and doing some fancy layout using CSS, but I’m not very good at CSS and we need the slides in PowerPoint format because other folks at the office add additional slides to the presentations I generate.
Around this time, I was reading tweets about new development on the patchwork package for arranging ggplot objects, and thought that instead of including multiple images on one slide, I could just create a multi-image layout in R and then render that layout to the PowerPoint slide.
In this post, I show how to combine plots and tables using patchwork to create multi-image PowerPoint slides using R Markdown.
I’m going to use data from the gapminder package, so if you don’t have it installed:
On my slide, I want to include 2 plots and 1 table related to life expectancy of certain counties in East Africa. I’m going to start by making the plots and then I’ll combine them in a single layout, and finally render the image to PowerPoint.
First, I load the needed packages and filter the gapminder data to the five countries in East Africa I’m going to be examining.
library(tidyverse) library(gapminder) library(scales) library(gridExtra) library(patchwork) my_countries <- c("Zambia", "Malawi", "Mozambique", "Tanzania", "Kenya") east_africa <- gapminder %>% filter(country %in% my_countries) %>% mutate(country = fct_reorder2(country, year, lifeExp)) # reorder for plotting
If you haven’t worked with the gapminder data before, take a peak. There is one row per country per year, with life expectancy, population, and GDP for each observation.
head(east_africa) ## # A tibble: 6 x 6 ## country continent year lifeExp pop gdpPercap ## <fct> <fct> <int> <dbl> <int> <dbl> ## 1 Kenya Africa 1952 42.3 6464046 854. ## 2 Kenya Africa 1957 44.7 7454779 944. ## 3 Kenya Africa 1962 47.9 8678557 897. ## 4 Kenya Africa 1967 50.7 10191512 1057. ## 5 Kenya Africa 1972 53.6 12044785 1222. ## 6 Kenya Africa 1977 56.2 14500404 1268.
The first plot I’m going to make is a bar chart of life expectancy by country for the most recent year of data, 2007.
bar <- east_africa %>% filter(year == max(year)) %>% # most recent year only ggplot(aes(x = country, y = lifeExp, fill = country)) + geom_col(width = 0.75, alpha = 0.9) + geom_text( aes(label = number(lifeExp, 0.1)), position = position_stack(vjust = 0.5), color = "white", fontface = "bold", size = 5 ) + scale_fill_brewer(palette = "Dark2") + scale_y_continuous(expand = expand_scale(0.01, 0.05)) + # remove extra space between bars and x-axis labels labs(y = "Life Expectancy (years)") + theme_minimal(base_size = 16) + theme( legend.position = "none", axis.title.x = element_blank(), axis.title.y = element_text(size = 10), axis.title.y.left = element_text(margin = margin(r = 10)), panel.grid.minor = element_blank(), panel.grid.major.x = element_blank() ) bar
Next, a line chart to look at the trend in life expectancy by country over time.
line <- east_africa %>% ggplot(aes(x = year, y = lifeExp, color = country)) + geom_line(lwd = 1.25, key_glyph = "timeseries") + # for those cute glyphs in the legend scale_color_brewer(palette = "Dark2") + labs(y = "Life Expectancy (years)") + theme_minimal(base_size = 16) + theme( legend.position = "bottom", legend.title = element_blank(), axis.title.x = element_blank(), axis.title.y = element_text(size = 10), axis.title.y.left = element_text(margin = margin(r = 10)), panel.grid.minor = element_blank(), plot.margin = margin(t = 30) ) line
Lastly, a table with life expectancy, population, and GDP for each country for the most recent year of data. To make a table that I can combine with the above ggplots, I’ll use
gridExtra::tableGrob(), which renders tables as grobs (grid graphical objects).
tab <- east_africa %>% filter(year == max(year)) %>% transmute( Country = country, Population = comma(pop), `GDP per capita` = dollar(gdpPercap, 1), `Life Expectancy` = number(lifeExp, 0.1), ) %>% arrange(Country) %>% tableGrob(theme = ttheme_minimal(), rows = NULL) grid.arrange(tab)
Alright, I’ve got the three graphical elements I want to combine into one layout on a single slide. Now comes the magical patchwork package to assemble these elements into one graphic.
I’m thinking this will look best with the bar chart and table side-by-side on the top half of the slide and the line chart below them. To do this, I take the elements created above and describe their arrangement using the fun patchwork syntax. Then, I’ll add a title and caption to finish off the layout.
layout <- (bar + tab) / line layout + plot_annotation( title = "Life Expectancy of Selected Countries in East Africa", caption = "Source: gapminder: Data from Gapminder github.com/jennybc/gapminder gapminder.org/data/", theme = theme(plot.title = element_text(size = 20, hjust = 0.5, face = "bold")) )
All that’s left to do is to get this onto a PowerPoint slide!
First, create a new R Markdown document and specify
powerpoint_presentation as the output format in the YAML header:
--- title: "Multi-image slide layouts in PowerPoint via R Markdown" author: "Matt Herman" output: powerpoint_presentation: reference_doc: template.pptx ---
If you want to use a PowerPoint template, you can specify a reference document in the header. This can be useful if you need to match a corporate brand or theme.
Then, in the body of the R Markdown file, create a code chunk with all the above code to create the plots and layouts. Finally, click the Knit button from RStudio or run
rmarkdown::render() to generate the PowerPoint presentation. And here is a screenshot of resulting presentation:
One thing that took a little trial and error was setting the
fig.height options of the knitr chunk as well as the dimensions of the content area in the PowerPoint template so that the patchwork layout fills the slides appropriately.
This example demonstrated how to make a singe PowerPoint slide with multiple images using patchwork. But of course, one of the super powers of programmatically creating slides is that you can very easily create a second or third or fourth slide with other countries specified. One nice way to implement this would be to use a parameterized child document and iterate through different sets of countries. (Next blog post, perhaps?)