How to Make a Donut Chart in ggplot

A lot of data visualizations are designed to show multiple data points (think bar charts that compare results for multiple groups). But what if you want to show just one data point? Say, for example, you want to show what percentage of people have accomplished something?

There are two techniques we often use in our consulting work to show single data points:

  1. Donut charts: These are pie charts with a hole in the middle (hence, donuts). Pie charts with multiple slices are problematic (people have trouble judging the relative sizes of the slices), but pie charts with one slice are very effective.
  2. Big numbers: This technique, which puts a single outcome in a big number in order to highlight it, is often overused, but done well, can be a very effective communication tool.

In our consulting work, we often combine donut charts and big numbers. Rather than just writing, “65% of people did X,” we make something like this:

We’ve made donut charts with big numbers regularly enough that I decided it was time to develop a function for it. Here’s how I did it using ggplot2.

Creating a Function to Make a Donut Chart with Big Number Function in ggplot2

We begin by loading packages. I only need two packages: the tidyverse (which loads ggplot2 and the dplyr package, which I use for data wrangling) and the scales package, which I use to make the nicely formatted number in the hole of the donut.

library(tidyverse)
library(scales)

Next, let me show you the full function with comments throughout. As you can see, the overall process looks like this:

  1. Use the value argument to create a data frame that we use for plotting
  2. Use the percent() function to create a big number as a percentage
  3. Plot the donut chart and add the big number to the middle of it
big_number_donut_plot <- function(value, font_family, highlight_color) {
  
  # Wrangle data to get a data frame in the format we need it in to make our donut chart
  df <- tibble(x = 1, y = value) %>% 
    mutate(y_negative = 1 - y) %>% 
    pivot_longer(cols = -x) 
  
  # Create a nicely formatted big number to go in the donut hole
  big_number_text_label <- percent(value, accuracy = 1)
  
  # Create our plot
  ggplot(df,
         aes(x = x,
             y = value,
             fill = name)) +
    
    # Add a bar, but don't add the legend
    geom_col(show.legend = FALSE) +
    
    # A pie/donut chart is a bar chart with polar coordinates
    # Add polar coordinates and set the direction to -1 
    # so the filled in part starts at the top and goes clockwise
    coord_polar(theta = "y",
                direction = -1) +
    
    
    # Set the limits, which is important for adding the hole
    xlim(c(-2, 2)) +
    
    # Set a color scale with the highlighted section in whatever color
    # is chosen with the highlight_color argument and the rest in a light gray
    scale_fill_manual(values = c(highlight_color, "grey90")) +
    
    # Set theme_void() to remove grid lines and everything else from the plot
    theme_void() +
    
    # Add the big number in the center of the hole
    annotate("text",
             label = big_number_text_label,
             family = font_family,
             fontface = "bold",
             color = highlight_color,
             size = 12,
             x = -2,
             y = 0)
  
}

In order to use the function, we’d write code like this. We use three arguments:

  1. value is the number (from 0 to 1) that we want to highlight. For this function, I assume it’s a percentage (you could adjust the function for non-percentages).
  2. font_family to use any font for the big number (of course, you have to have the font installed in order for it to work).
  3. highlight_color sets the color of the donut chart and the big number. I’ve used orange here.
big_number_donut_plot(value = 0.65, 
                      font_family = "Inter",
                      highlight_color = "orange")

And we get this.

Let’s break apart this function piece by piece in order to see how it works. I’ve written code and recorded videos to show each step.

Create our data frame

First, we create our data frame. Here’s a video walkthrough with the code used in the video below that.

value <- 0.65

# Wrangle data to get a data frame in the format we need it in to make our donut chart
df <- tibble(x = 1, y = value) %>% 
  mutate(y_negative = 1 - y) %>% 
  pivot_longer(cols = -x) 

df
## # A tibble: 2 × 3
##       x name       value
##   <dbl> <chr>      <dbl>
## 1     1 y           0.65
## 2     1 y_negative  0.35

Create the donut chart

Next, we create the donut chart. I’m creating it without the big number in the middle first. Video and code follow.

highlight_color <- "orange"

# Create our plot
ggplot(data = df,
       aes(x = x,
           y = value,
           fill = name)) +
  
  # Add a bar, but don't add the legend
  geom_col(show.legend = FALSE) +
  
  # A pie/donut chart is a bar chart with polar coordinates
  # Add polar coordinates and set the direction to -1 
  # so the filled in part starts at the top and goes clockwise
  coord_polar(theta = "y",
              direction = -1) +
  
  # Set the limits, which is important for adding the hole
  xlim(c(-2, 2)) +
  
  # Set a color scale with the highlighted section in whatever color
  # is chosen with the highlight_color argument and the rest in a light gray
  scale_fill_manual(values = c(highlight_color, "grey90")) +
  
  # Set theme_void() to remove grid lines and everything else from the plot
  theme_void() 

Add the big number in the middle of our donut chart

Finally, we create the big number and add it to the middle of our donut chart.

font_family <- "Inter"

# Create a nicely formatted big number to go in the donut hole
big_number_text_label <- percent(value, accuracy = 1)

# Create our plot
ggplot(data = df,
       aes(x = x,
           y = value,
           fill = name)) +
  
  # Add a bar, but don't add the legend
  geom_col(show.legend = FALSE) +
  
  # A pie/donut chart is a bar chart with polar coordinates
  # Add polar coordinates and set the direction to -1 
  # so the filled in part starts at the top and goes clockwise
  coord_polar(theta = "y",
              direction = -1) +
  
  
  # Set the limits, which is important for adding the hole
  xlim(c(-2, 2)) +
  
  # Set a color scale with the highlighted section in whatever color
  # is chosen with the highlight_color argument and the rest in a light gray
  scale_fill_manual(values = c(highlight_color, "grey90")) +
  
  # Set theme_void() to remove grid lines and everything else from the plot
  theme_void() +
  
  # Add the big number in the center of the hole
  annotate(geom = "text",
           label = big_number_text_label,
           family = font_family,
           fontface = "bold",
           color = highlight_color,
           size = 12,
           x = -2,
           y = 0)

And there we have it: a donut chart with a big number in the middle. I can now use this function for any other projects we work on. And, if you’re looking to do something similar, by all means, grab the code and run with it!

Want articles like this in your email? Sign up for the R for the Rest of Us newsletter.

Have any questions? Put them below. David or Charlie Hadley will help you out!