Skip to contents

This is a modification of ggplot2::position_stack() for creating diverging bar charts. In order to use this function, you must set a fill aesthetic (and that aesthetic should probably be a factor). This function will automatically break your chart into negative and positive values and display them in the same order as your fill levels.

Usage

position_diverge(vjust = 1, break_after = NULL, fill = FALSE, reverse = FALSE)

Arguments

vjust

Vertical adjustment for geoms that have a position (like text or points), not a dimension (like bars or areas). Set to 0 to align with the bottom, 0.5 for the middle, and 1 (the default) for the top.

break_after

Either an integer index or character value that represents the last positive level. The default, NULL, will split the levels halfway (with fewer positive levels if the total number of levels is odd).

fill

If TRUE will automatically scale bars to 100% as with position_fill()

reverse

If TRUE, will reverse the default stacking order.

Examples

library(ggplot2)

# Example data
test_df <- tibble::tribble(
  ~q,  ~response,  ~prop,
  'a', 'Yes',      0.25,
  'a', 'Mostly',   0.25,
  'a', 'Somewhat', 0.25,
  'a', 'Not Yet',  0.25,
  'b', 'Yes',      0.4,
  'b', 'Mostly',   0.3,
  'b', 'Somewhat', 0.2,
  'b', 'Not Yet',  0.1
  ) |>
  dplyr::mutate(
    response = forcats::fct_inorder(response),
    q = forcats::fct_inorder(q)
  )

# Default diverging with text
# In interactive use, this can also be run with `position = "diverge"`

test_df |>
  ggplot(aes(prop, q, fill = response)) +
  geom_col(position = position_diverge()) +
  geom_text(aes(label = scales::percent(prop,)),
            position = position_diverge(vjust = 0.5)) +
  geom_vline(xintercept = 0) +
  tntp_style(family = "sans") +
  # Reverse legend to match horizontal bar order
  guides(fill = guide_legend(reverse = TRUE)) +
  # Adjust axis labels to be positive on both sides
  scale_x_continuous(labels = ~scales::percent(abs(.)))


# Custom breaks with the break_after parameter
test_df |>
  ggplot(aes(q, prop, fill = response)) +
  geom_col(position = position_diverge(break_after = 'Yes')) +
  geom_hline(yintercept = 0) +
  tntp_style(family = "sans") +
  # Adjust axis labels to be positive on both sides
  scale_y_continuous(labels = ~scales::percent(abs(.)))