R ggplot Cookbook

This document is cookbook for R ggplot2 and ggplot2 add-on packages (such as cowplot) for recipes that I've developed and those I've had to repeatedly look up.

A couple notes about the code examples:

  • All examples assume the ggplot2 library has been imported
  • At the end of each example subsection, I list the versions of R, ggplot2 and all other packages used to create each example at the time of writing
  • In the code examples, p or any p* (e.g. p1, p2, pall) variable is a ggplot2 object

This document will be updated periodically.

Last updated: 2020-07-31

Plot types

Ribbon plots (fill between between two lines)

Ribbon geom_ribbon() plots can also be used to fill between two lines or point/scatter plots

# Data must be in wider (spread) format

ggplot(plot_data, aes(xvar)) +
  # Providing 'color' automatically creates a label
  geom_line(aes(y = yvar1, color = "Label 1"), size = 0.75) +
  geom_line(aes(y = yvar2, color = "Label 2"), size = 0.75) + 

  # 'geom_ribbon' fills between a 'ymin' and 'ymax' value. Adding
  # a transparency (alpha) value makes the plot look better.
  geom_ribbon(aes(ymin = yvar1, ymax = yvar2), alpha = 0.20) +

R: 3.6.2, ggplot: 3.2.1

Correlation visualization

Use ggplot2 extension GGally. Package function ggpairs can produce a variety of matrix-type plots between variables.

fpp3::us_change %>%
  GGally::ggpairs(columns = 2:6)

R: 4.0.2 ◊ ggplot2: 3.3.2 ◊ GGally: 2.0.0

Plot aesthetics


Add x,y axis labels

# Using labs
p + labs(x = "x label",
         y = "y label")

# Using xlab and ylab
p + xlab("x label") + ylab("y label")

R: 3.6.2 ◊ ggplot2: 3.2.1

Remove x,y axis titles

Example 1: Remove labels but keep the space that the axis labels took up

p + labs(x = "x label",
         y = "y label")

Example 2: Remove labels and remove the space that the axis labels took up

p + labs(x = NULL,
         y = NULL)

R: 4.0.2 ◊ ggplot2: 3.3.2

Put axis into scientific notation

Provide a labels function to scale_y_continuous() that converts the default labels to scientific SI notation.

The format_si() function is based on code by Ben Tupper. See https://stat.ethz.ch/pipermail/r-help/2012-January/299804.html.

format_si <- function(...) {

  function(x) {
    limits <- c(1e-24, 1e-21, 1e-18, 1e-15, 1e-12,
                1e-9,  1e-6,  1e-3,  1e0,   1e3,
                1e6,   1e9,   1e12,  1e15,  1e18,
                1e21,  1e24,  1e27,  1e30,  1e33)
    prefix <- c("y",   "z",   "a",   "f",   "p",
                "n",   "ยต",   "m",   " ",   "k",
                "M",   "G",   "T",   "P",   "E",
                "Z",   "Y",   "kY",  "MY",  "GY")

    # Vector with array indices according to position in intervals
    i <- findInterval(abs(x), limits)

    # Set prefix to " " for very small values < 1e-24
    i <- ifelse(i==0, which(limits == 1e0), i)

    paste(format(round(x/limits[i], 1),
                 trim=TRUE, scientific=FALSE, ...),

p + scale_y_continuous(labels = format_si())

R: 3.6.2 ◊ ggplot: 3.2.1

Put y-axis into percentage format

p + scale_y_continuous(labels = scales::percent)

R: 3.6.2 ◊ ggplot2: 3.2.1

Make y-axis origin start at zero

p + scale_y_continuous(expand = c(0, 0), limits = c(0, NA))

The same logic applies to the x-axis using scale_x_continuous

R: 4.0.2 ◊ ggplot2: 3.3.2


Add a legend to a single plot

Legend are automatically created if color (also spelled as colour) is assigned to the categorical or group variable. This requires data to be in the longer (gathered) format.

ggplot(mydataset, aes(xvar,yvar,color=groupvar)) +

Alternatively, if the data is in wider (spread) format, color can be assigned within each individual aesthetic with the legend label of choice.

# Data must be in wider (spread) format.
# Manually assign labels through the 'color' variable.

ggplot(mydataset, aes(x = xvar))+
  geom_line(aes(y = yvar1, color = "Label 1")) +
  geom_line(aes(y = yvar2, color = "Label 2"))

R: 3.6.2 ◊ ggplot: 3.2.1

Add a common legend to multiple plots (cowplot)

Recipe assumes that all plots share the same legend


  1. Create individual plots without legends (removed using theme(legend.position = "none"))
  2. Create a common legend by taking the legend details from one of the plots. Each individual plot should have the same legend therefore it doesn't matter which one we grab. Change the parameters to make it visible and put in the shape desired (e.g. single row for plot bottom)
  3. Create a cowplot::plot_grid for just the plots
  4. Create a new cowplot::plot_grid with the first plot grid and the legend
# Create the individual plots without legends
p1 <- p1 + theme(legend.position = "none")
p2 <- p2 + theme(legend.position = "none")
p3 <- p3 + theme(legend.position = "none")
p4 <- p4 + theme(legend.position = "none")

# Create the common legend
legend <- get_legend(
  p1 + 
    # Make the legend visible. Bottom alignment.
    theme(legend.position = "bottom") +
    guides(color = guide_legend(nrow = 1))

# Plot grid of just the plots
pall <- cowplot::plot_grid(p1, p2, p3, p4)

# Plot grid of the the plot grid defined above and the legend 
plot_grid(prow, legend_b, ncol = 1, rel_heights = c(1, .1))

R: 4.0.2 ◊ ggplot2: 3.3.2 ◊ cowplot: 1.0.0

Overwrite or change automatic legend labels

Overwrite the labels. The labels must be provided in the same alphabetical order as the default legend.

Method 1: Use scale_color_discrete()

p + scale_color_discrete(labels = c("label1", "label2", "label3"))

Method 2 (for point or line plot): Use scale_color_manual()

p + scale_color_manual(labels = c("label1", "label2", "label3"))

Method 2 is required when we also need to also change the color palette, e.g.

p + scale_color_manual(labels = c("label1", "label2", "label3"),
                       values = cbPalette)

We can't use both scale_color_discrete() and scale_color_manual() The two methods compete with eachother.

Method 3 (for fill-type plots): Use scale_color_manual()

As Method 2, but for fill-type plots.

p + scale_color_manual(labels = c("label1", "label2", "label3"))

# Or if we also need to specify a color palette
p + scale_color_manual(labels = c("label1", "label2", "label3"),
                       values = cbPalette)

R: 4.0.1 ◊ ggplot2: 3.3.2

Move a legend to different location

Legend position is set through the parameter legend.position within theme(). The variable accepts words, e.g. "top", "bottom", "left", "right", or relative x,y coordinates such as c(0.8,0.8).

The new legend will be automatically reshaped for its new location, e.g. a legend.position of "right" (default) will create a column-like legend, a legend.position of "bottom" will create a horizontal-like legend.

# Example 1
p + theme(legend.position = "top")

# Example 2
p + theme(legend.position = c(0.8,0.8))

R: 3.6.2 ◊ ggplot2: 3.2.1

Remove entire legend

p + theme(legend.position = "none")

R: 3.6.2 ◊ ggplot: 3.2.1

Remove legend title

p + theme(legend.title = element_blank())

R: 3.6.2 ◊ ggplot: 3.2.1

Change legend title

Example 1: For point- or line-like plots

# For geom_line, either
p + labs(color="New Legend Title")
# or
p + labs(color=guide_legend(title="New Legend Title"))

Example 2: For fill-like plots

# For geom_boxplot, either 
p + labs(fill="New Legend Title")
# or 
p + labs(fill=guide_legend(title="New Legend Title"))

R: 3.6.2 ◊ ggplot2: 3.2.1

Change legend shape

Method 1: Specifying number of rows (e.g. put everything in a single row)

# set 'nrow = 1' for a single row (everything arranged horizontally)
p + guides(color = guide_legend(nrow = 2))

Method 2: Specify the box shape

p + theme(legend.box = "horizontal")

R: 3.6.2 ◊ ggplot2: 3.2.1


Put a portion of a title or subtitle in italics or bold font

Using library ggtext. Its theme element element_markdown() understands basic markup characters like * and **.


ggplot(iris, aes(Sepal.Length, Sepal.Width)) + 
geom_point() + 
    title = "Sepal length and sepal width of various *Iris* species",
    x = "Sepal length (cm)",
    y = "Sepal width (cm)"
  ) +
# Use ggtext::element_markdown() theme element(instead of element_text(). It
# understands basic markup characters like `*` (italics), and `**` (bold)
theme(plot.title = ggtext::element_markdown())

R: 4.0.2 ◊ ggplot2:3.3.2 &loz ggtext: 0.1.0

Change plot aesthetics

Change the default color palette (e.g. to a colorblind palette)

Automatic ggplot color choices can be overwritten by providing a color palette. For example, two color-blind palettes:

# Color blinds pallettes. Both palettes are identical except for the first
# color which is either grey or black.

# Colorblind palette with grey
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442",
                "#0072B2", "#D55E00", "#CC79A7")

# Colorblind palette with black
cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442",
                 "#0072B2", "#D55E00", "#CC79A7")

Example 1: For line and point colors use scale_color_manual()

p + scale_color_manual(values = cbPalette)

Example 2: For fills use scale_fill_manual

p + scale_fill_manual(values = cbPalette)

R: 4.0.2 ◊ ggplot2:3.3.2

Increase line thickness


R: 3.6.2 ◊ ggplot: 3.2.1

Combine multiple plots

Using 'grid' library

Package 'grid': from rdocumentation.org


# 'p1','p2' are ggplot objects
grid.draw(rbind(ggplotGrob(p1), ggplotGrob(p2), size = "last"))

This can be modified to write a PDF of the plot to disk


grid.draw(rbind(ggplotGrob(p1), ggplotGrob(p2), size = "last"))

R: 4.0.2 ◊ ggplot: 3.3.2 ◊ grid: 4.0.2

Using 'cowplot' library


# plot1, plot2, etc. are ggplot objects. Any number of these can be supplied as
# arguments.
cowplot::plot_grid(p1, p2, p3, p4, ncol = 2)

# Save plot to disk
cowplot::save_plot("myplot.png", p_allforecasts)

R: 4.0.2 ◊ ggplot: 3.3.2 ◊ cowplot: 1.0.0

Saving plots

Saving a plot as a png image

Function ggsave() will save the last rendered plot.

# By default ggsaves the last rendered plot
ggsave("myplot.png", width = 6, height = 4.25, units = "in")

R: 3.6.2 ◊ ggplot 3.2.1


Pass a variable name as a string

See ggplot reference: defining aesthetic mappings progromatically

Quick answer: use aes_string() instead of aes() to define the aethetic..

The passed variable string can be an expression. For example, assuming we had the variables INCOMING and OUTGOING in our dataset we could could pass yvar = "INCOMING - OUTGOING" to our function plotVariable()

# Example assumes we've already loaded a data.frame with the name 'dframe' into
# memory. 'xvar', 'yvar', and 'groupvar' are strings.
plotVariable <- function(xvar, yvar, groupvar){

  p <- ggplot(dframe, aes_string(xvar, yvar, color = groupvar) +


R: 3.6.2 ◊ ggplot: 3.2.1