ggstackplot

2025-01-31

# load the package
library(ggstackplot)

This vignette explores the various features of the ggstackplot package.

Main Arguments

x and y arguments

Vertical stack

Select variables to make a stack. The selection order translates to the order with which the plots are stacked. Any valid tidyselect selection and/or renaming are supported.

# select any number of variables to make the stack
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec, drat)
  )


# the selection order translates into stack order
mtcars |> 
  ggstackplot(
    x = mpg, y = c(drat, wt, qsec)
  )


# use any valid tidyselect selection syntax
mtcars |> 
  ggstackplot(
    x = mpg, y = c(4, "carb", starts_with("d"))
  )


# use any valid tidyselect renaming syntax to rename stack panels
mtcars |> 
  ggstackplot(
    x = c(`mpg [units]` = mpg), 
    y = c(`weight [tons]` = wt, `speed` = qsec, drat)
  )

Horizontal stack

Select multiple x variables to stack:

# all examples shown in this document work the same way for a horizontal
# stack, simply switch out the x and y assignments
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec, drat)
  )

palette argument

Set individual plot colors by providing an RColorBrewer palette. Color definition applies to the color and fill aesthetics as well as the actual axis colors.

# use the Set1 RColorBrewer palette
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    palette = "Set1"
  )

# likewise for the horizontal stack version
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec),
    palette = "Set1"
  )

color argument

Alternatively, set colors manually by supplying a character vector of colors:

# select any specific colors for each plot
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8")
  )

remove_na argument

This removes NA values so that lines are not interrupted. When remove_na is set to FALSE, breaks in lines may appear due to NA values.

library(dplyr)

# default (NAs are removed so lines are not interrupted)
mtcars |> 
  add_row(mpg = 22, wt = 5, qsec = NA) |>
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8")
  )


# explicit `remove_na` = FALSE
mtcars |> 
  add_row(mpg = 22, wt = 5, qsec = NA) |>
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    remove_na = FALSE
  )

both_axes argument

When both_axes = TRUE , the stacked variable axes are duplicated on both sides of each stacked plot.

# Vertical stackplot
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    both_axes = TRUE
  )


# Horizontal stackplot
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    both_axes = TRUE
  )

alternate_axes argument

When alternate_axes = FALSE , the axes for the multiple variables are kept on the same side of the facets. The default behavior alternates these axes left/right or top/bottom.

# axes do not alternate:
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    alternate_axes = FALSE
  )


# Horizontal version
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    alternate_axes = FALSE
  )

switch_axes argument

Determines whether to switch the stacked axes. Not switching means that for vertical stacks the plot at the bottom has the y-axis always on the left side; and for horizontal stacks that the plot on the left has the x-axis on top. Setting switch_axes = TRUE}, leads to the opposite. If alternate_axes = TRUE this essentially switches the order with which the axes alternate (e.g., right/left/right vs. left/right/left). Note that if both_axes = TRUE, neither the switch_axes nor alternate_axes parameter has any effect.

# stacked axis starts on the right
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    switch_axes = TRUE
  )


# or for the horizontal version, stacked axis
# starts on the bottom
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    switch_axes = TRUE
  )


# and in combination with alternate_axes = FALSE
# all axes on the right
mtcars |> 
  ggstackplot(
    x = mpg, y = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    alternate_axes = FALSE,
    switch_axes = TRUE
  )


# or all axes on the top
mtcars |> 
  ggstackplot(
    y = mpg, x = c(wt, qsec),
    color = c("#E41A1C", "#377EB8"),
    alternate_axes = FALSE,
    switch_axes = TRUE
  )

overlap argument

Overlap determines the grid overlap between the multiple stacked plots. 1 corresponds to fully overlapping (similar to having a ggplot sec_axis enabled) while 0 does not overlap at all.

# define any overlap between 0 and 1
mtcars |> 
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 0.3
  )


# full overlap
mtcars |> 
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 1
  )

Different overlaps

Multiple overlap arguments can be supplied with a numeric vector of numbers between 0 and 1, where each element in the vector corresponds to the overlap between the n and n+1th overlap value. For example, for a plot with four stacked panels: qsec, drat, wt, hp, a vector of overlap = c(1, 0, 1) indicates that between the first 2 elements (qsec and drat) there is full overlap. Between drat and wt there is no overlap (0). Between wt and hp there is full overlap.

# different overlap between stack panels
mtcars |> 
  ggstackplot(
    x = mpg, 
    y = c(qsec, drat, wt, hp),
    color = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3"),
    overlap = c(1, 0, 1)
  )


# and the horizontal version
mtcars |> 
  ggstackplot(
    y = mpg, 
    x = c(qsec, drat, wt, hp),
    color = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3"),
    overlap = c(1, 0, 1)
  )

shared_axis_size argument

The size of the shared axis determines the size of any shared axes relative to the grid size of the original ggplot. The size of the shared axis often needs to be adjusted depending on which aspect ratio is intended. It is defined as fraction of a full panel, between 0 and 1.

mtcars |> 
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 1,
    # can be only 10% of a plot size as we're overlapping plots
    shared_axis_size = 1
  )

simplify_shared_axis argument

Sometimes it’s better just to keep the shared axis on each panel. This produces something akin to a facet_wrap() or cowplot::plot_grid().

mtcars |> 
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    simplify_shared_axis = FALSE
  )


# also goes well with changing `both_axes`, `switch_axes` and/or `alternate_axes`
mtcars |> 
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    simplify_shared_axis = FALSE,
    alternate_axes = FALSE
  )

The template argument

This is the most important argument. It defines which ggplot to use as the template for all plots in the stack. This can be an actual plot (just the data will be replaced) or a ggplot that doesn’t have data associated yet. The possibilities are pretty much endless. Just make sure to always add the theme_stacked_plot() base theme (you can modify it more from there on). A few examples below:

Theme modifications

Add any modification to the overlying theme as you see fit.

Here, template allows the user to define that a ggplot() will serve as the base, with geom_line as the primary geom. Then, theme_stackplot() is applied and custom theme() options are set.

library(ggplot2)

# increase y axis text size
mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    template = 
      ggplot() + 
      geom_line() +
      theme_stackplot() +
      theme(
        axis.title.y = element_text(size = 20),
        axis.text.y = element_text(size = 16)
      )
  )


# increase the panel margins
mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    template = 
      ggplot() + 
      geom_line() +
      theme_stackplot() +
      theme(
        # increase left margin to 20% and top/bottom margins to 10%
        plot.margin = margin(l = 0.2, t = 0.1, b = 0.1, unit = "npc")
      )
  )

Grid modifications

Modifying the panel.grid argument can create gridlines for both the stacked variable axes and the shared axis. This can get a bit cluttered in a plot where overlap = 1.

mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 1,
    template = ggplot() +
      geom_line(data = function(df) filter(df, .yvar == "qsec")) +
      geom_point(data = function(df) filter(df, .yvar == "drat")) +
      theme_stackplot() +
      theme(
        panel.grid.major = element_line(
          color = "lightgray", 
          linewidth = 0.8)
      )
  )

But, this can look reasonable if there is no overlap of the stacked plats, and/or if the lines are made inconspicuous:

mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 0,
    template = ggplot() +
      geom_line(data = function(df) filter(df, .yvar == "qsec")) +
      geom_point(data = function(df) filter(df, .yvar == "drat")) +
      theme_stackplot() +
      theme(
        panel.grid.major = element_line(
          color = "lightgray", 
          linetype = "dotted", 
          linewidth = 0.5)
      )
  )

Other themes

You aren’t bound to our theme’s aesthetic choices :), you can always add another theme or theme modifications on top of theme_stackplot()! Here we add the classic theme_bw() to get those nice clean gridlines back, as well as a panel border.

mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 0,
    template = ggplot() +
      geom_line(data = function(df) filter(df, .yvar == "qsec")) +
      geom_point(data = function(df) filter(df, .yvar == "drat")) +
      theme_stackplot() +
      theme_bw() # give us that good theme!
  )

Custom geom data

It is possible to use different geoms for different stacked panels. Here, we use both lines and points. These geoms are defined in the template argument.

# use different geoms for different panels
# you can refer to y-stack panel variables with `.yvar` and x-stack panel variables with `.xvar`
mtcars |>
  ggstackplot(
    x = mpg, y = c(qsec, drat),
    color = c("#E41A1C", "#377EB8"),
    overlap = 1,
    template = ggplot() +
      geom_line(data = function(df) filter(df, .yvar == "qsec")) +
      geom_point(data = function(df) filter(df, .yvar == "drat")) +
      theme_stackplot()
  )