In this post, you will learn how to create PowerPoint presentations using R and RMarkdown. There are other presentation options in RMarkdown and RStudio such as ioslides, slidy, and beamer, but for this post, we will focus on PowerPoint presentations. 

https://youtu.be/L0o4R0tuodI
Watch step-by-step instructions on YouTube

We will see three ways of creating PowerPoint presentations:

  1. Using the default RMarkdown option
  2. Using a custom PowerPoint template
  3. Using officedown/officer R libraries for further customization

Before we jump into creating these presentations, I would like to share a quick overview of PowerPoint and presentation design best practices from two books: Presentation Zen and Strategic Storytelling.

Presentation Zen

Garr Reynolds, the author of Presentation Zen, recommends the use of full-bleed background images with one sentence, a word, or statistic. He also recommends using the rule of thirds to place the point of focus in the intersection of lines dividing the slide into three equal sections horizontally and vertically. We will see whether we can make that happen. He also suggests using simple charts to make your point. Although these types of presentations are better for keynotes, we will try to apply some of these principles.

Strategic Storytelling

Dave McKinsey, the author of Strategic Storytelling, provides examples from top management consulting companies such as McKinsey and Accenture. He explains why these business presentations work. Some of the main points from this book are:

  1. Use of the Minto principle
  2. Use the slide title to make your point or ask a question
  3. Use of body content to support your point mentioned in the title

Let’s talk about the Minto principle also called the Minto Pyramid.

Barbara Minto was McKinsey consulting’s first female MBA professional. She invented the Minto pyramid to help consultants make effective business presentations. It provides a good structure for organizing your presentation content. You start with your main idea or recommendation. Then provide the supporting arguments with data and facts. Each supporting argument is further supported by more arguments and data. If you notice any of the consulting company’s presentations, you will see they have many charts, call-outs, and notes with references. 

With these presentation design best practices, let’s dive into creating PowerPoint presentations with R and RMarkdown.

For this post, let’s say that we are studying the streaming services market. Our client is a company trying to enter in this market.

Here’s how we will structure the presentation:

  • Slide 2: Our main recommendation.
  • Slide 3: Survey of the market: share prices
  • Slide 4: Survey of the market: market share
  • Slide 5: Opportunities
  • Slide 6: Side by side comparison of Netflix and Disney+
  • Slide 7: Conclusion
PowerPoint presentation created in R example

Let’s get started.

First, let’s make sure you can build the example PowerPoint presentation in RStudio.

To start a new markdown document, click on File, New File, R Markdown, then Presentation, and finally PowerPoint. 

The title is of my presentation is “Streaming Services Market Study” and the author is “Best Brians Consulting” (It is Brians and not brains after the movie Life of Brian)

Make sure that this document compiles and gives you a nice PowerPoint presentation. 

Click on the “knit” button. Save your file. If everything works out, you should have a new PowerPoint presentation. 

If everything worked out, edit the RMarkdown document with the new content for this streaming services presentation.

First, let’s change the knitr options to cache the data and suppress warnings and messages.

```{r setup, include=FALSE} knitr::opts_chunk$set(echo = FALSE, cache = TRUE, warning = FALSE, message = FALSE) ```
Code language: R (r)

The first slide has recommendations. 

## Recommendations - This is a tough market with many established companies - You will need a unique strategy to set yourself apart - We recommend you enter in a narrow market and not compete with the big companies
Code language: R (r)

Our next slide has data with the share prices of the companies in this space.

Following the “Strategic Storytelling” book example, the title of our slide is: Companies such as Netflix have cemented their roles as streaming entertainment providers

This R code pulls the historical share prices of Netflix, Disney, and Comcast, ATT, and shows a ggplot multiple line plot. It could be made prettier and more valuable with annotations, but for this exercise, it is good enough.

## Companies such as Netflix have cemented their roles as streaming entertainment providers ```{r sharepricesplot, fig.asp=0.6, dpi=300} library(tidyquant) library(lubridate) library(ggplot2) library(ggthemes) library(dplyr) my_tickers <- c("NFLX", "DIS", "T", "CMCSA") stock_prices <- tq_get(x = my_tickers, from = Sys.Date() %m-% years(5), to = Sys.Date(), get = "stock.prices") g <- ggplot(stock_prices, aes(x = date, y = close, group = symbol, color = symbol, label = symbol)) + geom_line() label_data <- tribble( ~date, ~symbol, ~close, as.Date("2019-01-02"), 'NFLX', 220, as.Date("2019-01-02"), 'DIS', 140, as.Date("2019-01-02"), 'CMCSA', 48, as.Date("2021-01-02"), 'T', 40, ) g + theme_wsj() + geom_text(data = label_data, aes(x = date, y = close, label = symbol, color = symbol)) + ggtitle(label = "Daily Closing Share Prices") + theme(legend.position = "none", title = element_text(size = rel(1.3), family = "Arial"), plot.title.position = "plot") ```
Code language: R (r)
A multiple line plot with share prices for a PowerPoint presentation created using R and RMarkdown

Our next slide is the market share data.

This R code creates a treemap of market share data using ggplot and treemapify. This chart could also be made better, but this will do for now.

## The market is packed with many streaming options ```{r marketshare, fig.asp=0.6, dpi=300} market_share <- tribble( ~company, ~share, "Netflix", 0.2, "Amazon Prime", 0.16, "Hulu", 0.13, "Max", 0.12, "Disney", 0.11, "Apple", 0.05, "Peacock", 0.05, "ESPN", 0.04, "Starz", 0.03, "Showtime", 0.03, "Paramount", 0.03, "YouTube TV", 0.01, "Sling TV", 0.01, "BritBox", 0.01, "Other", 0.02, ) library(treemapify) library(scales) ggplot(market_share, aes(area = share, fill = company, label = paste(company, percent(share), sep = "\n"))) + geom_treemap() + geom_treemap_text(color = "black", place = "center", grow = F) + scale_fill_brewer(palette = "Set3") + labs(title = "Market Share of Streaming Services", caption = "Source: The Wrap. https://www.thewrap.com/netflix-streaming-us-market-share-chart") + theme(legend.position = "none", plot.title = element_text(face = "bold"), plot.caption = element_text(hjust = 0), plot.title.position = "plot", plot.caption.position = "plot") ```
Code language: R (r)
A tree map created using R and Rmarkdown for PowerPoint

The next slide is a chart from a website showing opportunities. I have used include_graphics from knitr to add the image. I have also added a caption by using fig.cap option.

## ... but it is possible to pull some customers away ```{r streamingservicescomparision, fig.cap="Although Netflix still is the market leader, it dropped from 29% to 20% from 2020 to 2021. Source: https://www.thewrap.com/netflix-streaming-us-market-share-chart"} knitr::include_graphics(path = "https://www.thewrap.com/wp-content/uploads/2021/03/040221-U.S.-Streaming-Market-Share-2020-versus-2021.png") ```
Code language: R (r)

Next up is a case study of Disney+ vs. Netflix. I am using a two-column layout using a special Pandoc syntax. Within each column, I have a column header and a bulleted list. I also added the source in the first column.

## A case in point: Disney+ :::::: {.columns} ::: {.column} Netflix - 200M Subscribers - Started 13 years ago - Projected to get 300M subscribers Source: cnet ::: ::: {.column} Disney+ - 95M Subscribers - Started 15 months ago - Projected to reach 250M subscribers ::: ::::::
Code language: R (r)

Lastly, we have the conclusion slide with one sentence. 

## Conclusion This is a tough market to break into, but you can carve a niche out. Don't go head-to-head with the big companies.
Code language: R (r)

After hitting knit, you will see that the PowerPoint presentation has the line plot and the treemap we generated using ggplot.

It also has an external image from a URL.

It created the two-column layout as well.

But you notice some other problems too.

  • Page numbers are missing.
  • The aspect ratio is not 16 by 9.
  • The slide titles are aligned in the center and the long title doesn’t fit neatly.
  • Both the charts are taking the same smallish space.
  • The conclusion slide looks bland.

We can resolve some of these issues by providing our custom PowerPoint template. 

  1. Create a new PowerPoint presentation. 
  2. Then edit the layouts. 
  3. Go to View and click on Slide Master.

Here you will see all the default layout options.

RMarkdown and Pandoc currently support only these four layouts:

  • title
  • title and content
  • section header, and
  • two-column layout

That means we can customize only these four layouts. 

  1. Edit the master slide first.
  2. Click on the “click to edit” text box.
  3. Change the font type to your liking. I selected “Lato” for the title.
  4. Next, I am selected Corbel for the body or content area.
  5. Add custom text boxes in the footer for date and page numbers.
  6. Insert the date and slide number from the Insert menu.
  7. Change the font type and size.
  8. Then delete all the existing boxes for footers.
  9. I added a logo in the center of the slide. I created this simple logo using Logomakr.com

Let’s also change the background color of the slides to something light. I chose #edf6f9 as the background color.

  1. Go to Background Styles. 
  2. Format Background. 
  3. Click on Color and then more colors.

Let’s also change the color of the slide title. I chose #006d77.

Save the PowerPoint template. Add a reference in the YAML in your RMarkdown to this PowerPoint template like this:

output: powerpoint_presentation: reference_doc: template.pptx
Code language: R (r)

Hit the “knit” button to see the results.

You can change the font type of the plots to match your presentation font type by using the “showtext” library. But I leave that for you.

If you have a simple presentation, this can work very well. You pull all your data in one place, create your charts, and add recommendations. You can edit the PowerPoint later, of course, but for repeatability, we want to keep as many things in RMarkdown as possible.

One disadvantage of this approach using Pandoc is that we are limited to only four layouts. If you wanted to add one title and one subtitle to your layout, it won’t work. Another disadvantage is an image or a table can’t exist with other content. If you add content to a slide that already has a chart or an image, it will get pushed to a new slide.

You can get around this problem using patchwork or similar libraries to place side-by-side graphs, tables, and text. But that may not give you the best resolution. We also can’t add random texts or call-outs.

There’s an option to add background images in HTML presentations, but it doesn’t work for PowerPoint.

Let’s explore the office and officedown libraries for further customization. You can use the officedown in YAML as officedown::rpptx_document and provide a template, but I don’t know the exact advantages of doing so.

Instead, we will create an R file and build the PowerPoint presentation one slide at a time.

Let’s load the officer and magrittr libraries.

library(officer) library(magrittr)
Code language: R (r)

Next, we will load our template file using the read_pptx function in a variable called my_ppt.

my_ppt <- read_pptx(path = "template.pptx")
Code language: R (r)

With the layout_summary function, we can check all the layouts available to us.

officer::layout_summary(my_ppt)
Code language: R (r)

Add a new layout to your PowerPoint template with one text box and increase the font size to 80. Rename this layout to ‘one big number’.

Save the template and read the file again.

Now when you run layout_summary you will see that this new layout is available to us.

The layout_properties function lets us see the available content areas we can change.

layout_properties(my_ppt, layout ="Title Slide")
Code language: R (r)

This is an important function. Notice the `ph type` argument value. We need to use the value of `ph type` to set the content.

It will be easier with an example.

my_ppt <- add_slide(my_ppt, layout = "Title Slide", master = "Office Theme") %>% ph_with(value = "Streaming Services Market Study", location = ph_location_type(type = "ctrTitle")) %>% ph_with(value = "Best Brians Consulting", location = ph_location_type(type = "subTitle"))
Code language: R (r)

Let’s create our first slide with the presentation title slide.

To add a slide we use add_slide function. To this function, we specify the layout and the theme to use.

To add content we use the ph_with function. The value argument of this function specifies the content.

And now the important part: where do we want the content to go? To find the location, we use ph_location_type function and pass the type we saw in ph type.

For the opening, title slide the box is called ctrTitle.

Let’s add our title.

Also, add the author to ph type of subtitle.

Let’s save this new presentation using the print command.

print(my_ppt, target = "officer-presentation.pptx")
Code language: R (r)

Open the PowerPoint and check whether you can see the title and subtitle.

Let’s add the second slide with recommendations. But check the available shape types using the layout_properties function for the Title and Content layout. You will that we have a title and body types available.

Let’s add ‘recommendations’ as the title and pass the bullet points for this slide as a vector to the body location.

my_ppt <- add_slide(my_ppt, layout = "Title and Content", master = "Office Theme") %>% ph_with(value = "Recommendations", location = ph_location_type(type = "title")) %>% ph_with(value = c("This is a tough market with many established companies", "You will need a unique strategy to set yourself apart", "We recommend you enter in a narrow market and not compete with the big companies"), location = ph_location_type(type = "body"))
Code language: R (r)

Save the document again and see what it looks like.

Next, add a slide with the share prices chart, but we need to create this chart first. This code will save the chart in a ggplot object called share_prices_plot.

my_ppt <- add_slide(my_ppt, layout = "Title and Content", library(tidyquant) library(lubridate) library(ggplot2) library(ggthemes) library(dplyr) my_tickers <- c("NFLX", "DIS", "T", "CMCSA") stock_prices <- tq_get(x = my_tickers, from = Sys.Date() %m-% years(5), to = Sys.Date(), get = "stock.prices") g <- ggplot(stock_prices, aes(x = date, y = close, group = symbol, color = symbol, label = symbol)) + geom_line() label_data <- tribble( ~date, ~symbol, ~close, as.Date("2019-01-02"), 'NFLX', 220, as.Date("2019-01-02"), 'DIS', 140, as.Date("2019-01-02"), 'CMCSA', 48, as.Date("2021-01-02"), 'T', 40, ) share_prices_plot <- g + theme_wsj() + geom_text(data = label_data, aes(x = date, y = close, label = symbol, color = symbol)) + ggtitle(label = "Daily Closing Share Prices") + theme(legend.position = "none", title = element_text(size = rel(1.3), family = "Arial"), plot.title.position = "plot")
Code language: R (r)

We will again use title and body locations to add this plot and the title. The only difference is that we can provide a resolution of the chart.

my_ppt <- add_slide(my_ppt, layout = "Title and Content", master = "Office Theme") %>% ph_with(value = "Companies such as Netflix have cemented their roles as streaming entertainment providers", location = ph_location_type(type = "title")) %>% ph_with(value = share_prices_plot, location = ph_location_type(type = "body"), res = 300)
Code language: R (r)

Next, let’s add the tree map with the market share

market_share <- tribble( ~company, ~share, "Netflix", 0.2, "Amazon Prime", 0.16, "Hulu", 0.13, "Max", 0.12, "Disney", 0.11, "Apple", 0.05, "Peacock", 0.05, "ESPN", 0.04, "Starz", 0.03, "Showtime", 0.03, "Paramount", 0.03, "YouTube TV", 0.01, "Sling TV", 0.01, "BritBox", 0.01, "Other", 0.02, ) library(treemapify) library(scales) market_share_tree_map <- ggplot(market_share, aes(area = share, fill = company, label = paste(company, percent(share), sep = "\n"))) + geom_treemap() + geom_treemap_text(color = "black", place = "center", grow = F) + scale_fill_brewer(palette = "Set3") + labs(title = "Market Share of Streaming Services", caption = "Source: The Wrap. https://www.thewrap.com/netflix-streaming-us-market-share-chart") + theme(legend.position = "none", plot.title = element_text(face = "bold"), plot.caption = element_text(hjust = 0), plot.title.position = "plot", plot.caption.position = "plot")
Code language: R (r)
my_ppt <- add_slide(my_ppt, layout = "Title and Content", master = "Office Theme") %>% ph_with(value = "The market is packed with many streaming options", location = ph_location_type(type = "title")) %>% ph_with(value = market_share_tree_map, location = ph_location_type(type = "body"), res = 300)
Code language: R (r)

With the default RMarkdown/Pandoc option, we can place an image and text side-by-side, but let’s try that with the officer package and the ‘Picture with Caption’ layout.

We can see we have body, title, and pic locations available for us with the ‘Picture with Caption’ layout.

Let’s add the year-over-year market share comparison chart from the wrap in the pic location and some text in the body location.

ph_wiyoy_market_comp_img <- file.path("wrap-yoy-market-share.png") my_ppt <- add_slide(my_ppt, layout = "Picture with Caption", master = "Office Theme") %>% ph_with(value = "... but it is possible to pull some customers away", location = ph_location_type(type = "title")) %>% ph_with(value = external_img(yoy_market_comp_img), location = ph_location_type(type = "pic")) %>% ph_with(value = "Although Netflix still is the market leader, it dropped from 29% to 20% from 2020 to 2021. Source: https://www.thewrap.com/netflix-streaming-us-market-share-chart", location = ph_location_type(type = "body"))
Code language: R (r)

Let’s add and save the file again.

I don’t like the location of the source text. I want to move it close to the footer.

To do so, I need to first create a style or properties I want to apply to this text.

We will use the fp_text function to make the text color light gray and set the font size to 8.

We will name this variable source_box_style. 

source_box_style <- fp_text(bold = FALSE, color = "gray50", font.size = 8)
Code language: R (r)

To add a text box at any location, we need to provide the text box location using the left, top, and other coordinates to the ph_location function. 

source_box_location <- ph_location( left = 1, top = 6.6, height = 0.3, width = 11, newlabel = "source_box" )
Code language: R (r)

And to get the text box to show up on the slide, we need to use the fpar function, which creates a paragraph object for PowerPoint. In fpar, we will wrap our source text and provide the style property we created earlier with the ftext function. 

source_box_location <- ph_location( left = 1, top = 6.6, height = 0.3, my_ppt <- add_slide(my_ppt, layout = "Picture with Caption", master = "Office Theme") %>% ph_with(value = "... but it is possible to pull some customers away", location = ph_location_type(type = "title")) %>% ph_with(value = external_img(yoy_market_comp_img), location = ph_location_type(type = "pic")) %>% ph_with(value = "Although Netflix still is the market leader, it dropped from 29% to 20% from 2020 to 2021.", location = ph_location_type(type = "body")) %>% ph_with(value = fpar(ftext(text = "Source: https://www.thewrap.com/netflix-streaming-us-market-share-chart", prop = source_box_style)), location = source_box_location)
Code language: R (r)

Here are the steps again to create a text box:

  1. Define style or properties
  2. Decide and provide the coordinates to ph_location function
  3. Format the given text using the style and ftext function, and
  4. finally wrap the ftext call in fpar function to create a paragraph object

You can find the location coordinates by creating a box in PowerPoint and adjusting it. To make it easier, I saved the location object in a variable called source_box_location. 

Save the PowerPoint presentation again. 

Using this same principle, let’s create a callout box and add it to the share prices slide. I created callout_box_style and callout_box_location.

callout_box_style <- fp_text(bold = TRUE, color = "black", font.size = 16, font.family = "Corbel") callout_box_location <- ph_location( left = 2.6, top = 3, height = 2, width = 2, newlabel = "call_out_box" )
Code language: R (r)

To select the slide with share prices, we need to use “on_slide” function and provide the slide number. 

my_ppt <- on_slide(my_ppt, index = 4) %>% ph_with(value = fpar(ftext(text = "Netflix had some stumbles but it is going up steadily", prop = callout_box_style)), location = callout_box_location)
Code language: R (r)

Make this change and then save the presentation again.

print(my_ppt, target = "officer-presentation.pptx")
Code language: R (r)

Of course, it would be easier to draw the text box later in the PowerPoint, but this gives an additional tool to place text boxes with dynamic content at our chosen locations. 

For the final addition, I would like to add a background image that bleeds into the slide and add some text on it. We will use the one big number layout we created earlier. 

Let’s check the layout properties. We see the property name as “one big number.”

I downloaded a photo from unsplash. I named it tv-3.jpg, whose path I am storing in bg_image_path variable. 

bg_image_path <- file.path("tv-3.jpg")
Code language: R (r)

Next, let’s add a slide using the add_slide function and “one big number” as the layout. 

Then we will use the ph_with function to place the external image and 13.38 as the width and height. We will use the ph_location_fullsize function to return the proper dimensions from the slide layout. 

Lastly, we will use another ph_with function to add the text of “31% drop” at the text placeholder location. 

bmy_ppt <- add_slide(my_ppt, layout = "one big number", master = "Office Theme") %>% ph_with(value = external_img(bg_image_path, width = 13.38, height = 13.38), location = ph_location_fullsize(), use_loc_size = FALSE) %>% ph_with(value = "31% drop", location = ph_location_type(type = "body")) print(my_ppt, target = "officer-presentation.pptx")
Code language: R (r)
powerpoint presentation r rmarkdown a quick demo

These steps should give you a good-looking PowerPoint presentation using RMarkdown. Here’s the summary of our process:

  • We created a PowerPoint presentation using the default knitr options in RMarkdown. It worked well but didn’t offer customization. 
  • Therefore, we created a PowerPoint template to make the presentation look better, and it did!
  • But for more control, we looked at the officer R package and created our layouts, and placed objects where we wanted them.

I hope this helps you when you are trying to create PowerPoint presentations using R and RMarkdown. Let me know what you think by leaving a comment. 

About the Author

A co-author of Data Science for Fundraising, an award winning keynote speaker, Ashutosh R. Nandeshwar is one of the few analytics professionals in the higher education industry who has developed analytical solutions for all stages of the student life cycle (from recruitment to giving). He enjoys speaking about the power of data, as well as ranting about data professionals who chase after “interesting” things. He earned his PhD/MS from West Virginia University and his BEng from Nagpur University, all in industrial engineering. Currently, he is leading the data science, reporting, and prospect development efforts at the University of Southern California.

>