Skip to contents

Overview

This article will demonstrate some fantastic ggplot2 extension packages.

patchwork

The patchwork package enables plots to be patched together.

A convenient hack to add a panel centred title can be to add a facet_wrap layer with a character title string for the facets argument.

Otherwise you will need to modify margins

library(patchwork)

p1 <- mtcars |> 
  gg_point(
    x = mpg, 
    y = disp,
    y_labels = replace_seq,
  ) +
  facet_wrap(~"Plot 1")

p2 <- mtcars |> 
  gg_boxplot(
    x = gear, 
    y = disp, 
    group = gear, 
    width = 0.5,
    y_labels = replace_seq,
  ) +
  facet_wrap(~"Plot 2")

p3 <- mtcars |> 
  gg_smooth(
    x = disp, 
    y = qsec, 
    x_breaks = scales::breaks_pretty(4), 
  ) +
  facet_wrap(~"Plot 3")

 (p1 + p2) / p3 +
  plot_annotation(
    title = "A collection of plots patched together",
    subtitle = "Works so nice!",
    theme = light_mode_r() +
      theme(
        plot.title = element_text(margin = margin(t = -11)),
        plot.subtitle = element_text(margin = margin(t = 5.5)),
      )
  )

ggtext

The ggtext package support using markdown in text.

penguins |>
  gg_point(
    x = bill_depth_mm,
    y = bill_length_mm,
    col = species,
    title = "***Pygoscelis*** *penguin* bill **lengths** and depths",
    subtitle = "<span style = 'color: #0095A8 ;'>**Adelie**</span>, 
       <span style = 'color:#FF7043;'>**Chinstrap**</span>, 
       *and* <span style = 'color:#112E51;'>**Gentoo**</span>", 
    mode = light_mode_n()
  ) +
  theme(plot.title = ggtext::element_markdown()) +
  theme(plot.subtitle = ggtext::element_markdown())

showtext

The showtext package enables the use of different fonts in plots. The *_mode_* theme functions provide a family argument.

head(sysfonts::font_families_google())
#> [1] "ABeeZee"       "Abel"          "Abhaya Libre"  "Abril Fatface"
#> [5] "Aclonica"      "Acme"

sysfonts::font_add_google("Covered By Your Grace", "grace")
sysfonts::font_add_google('Roboto Slab', 'roboto_slab')
sysfonts::font_add_google('Syne Mono', 'syne')

showtext::showtext_auto(enable = TRUE)

penguins |>
  mutate(across(sex, \(x) str_to_sentence(x))) |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
    col = sex,
    facet = species,
    title = "Penguins body mass by flipper length",
    subtitle = "Palmer Archipelago, Antarctica",
    caption = "Source: Gorman, 2020",
    mode = light_mode_r(family = "grace")
  ) +
  theme(
    plot.title = element_text(family = "roboto_slab"),
    plot.subtitle = element_text(family = "syne")
  )


showtext::showtext_auto(enable = FALSE)

gghighlight

The gghighlight package enables geoms or parts thereof to be highlighted.

penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
  ) +
  gghighlight::gghighlight(body_mass_g >= 5000)

The gg_* function builds the scale for the data that it thinks will be within the panel. Therefore in some situations, this must be taken into account to ensure the scale builds correctly.

penguins |>
  gg_histogram(
    x = flipper_length_mm,
    col = species,
    x_breaks = scales::breaks_pretty(4),
    col_palette = rep(blue, 3), 
  ) + #build the scale for not faceted (i.e. all data)
  facet_wrap(~species) + #then add facet layer
  gghighlight::gghighlight() 

ggforce

The ggforce package includes numerous extra geoms, stats etc.

library(ggforce)
geom_bspline()
#> geom_path: arrow = NULL, lineend = butt, na.rm = FALSE
#> stat_bspline: na.rm = FALSE, n = 100, type = clamped
#> position_identity

economics |>
  slice_head(n = 35) |> 
  gg_path(
    stat = "bspline", n = 100, type = "clamped",
    x = date, 
    y = unemploy,
    linewidth = 1,
  ) 

geom_mark_hull()
#> geom_mark_hull: na.rm = FALSE, expand = 5, radius = 2.5, concavity = 2, label.margin = c(2, 2, 2, 2), label.width = NULL, label.minwidth = 50, label.fontsize = 12, label.family = , label.lineheight = 1, label.fontface = c("bold", "plain"), label.hjust = 0, label.fill = white, label.colour = black, label.buffer = 10, con.colour = black, con.size = 0.5, con.type = elbow, con.linetype = 1, con.border = one, con.cap = 3, con.arrow = NULL
#> stat_identity: na.rm = FALSE
#> position_identity

penguins |>
  gg_blanket(
    geom = "mark_hull",
    x = flipper_length_mm,
    y = body_mass_g,
    col = species,
    coord = coord_cartesian(),
  ) +
  geom_point()

ggrepel

The ggrepel package can be used to neatly avoid overlapping labels.

library(ggrepel)
geom_text_repel()
#> geom_text_repel: parse = FALSE, na.rm = FALSE, box.padding = 0.25, point.padding = 1e-06, min.segment.length = 0.5, arrow = NULL, force = 1, force_pull = 1, max.time = 0.5, max.iter = 10000, max.overlaps = 10, nudge_x = 0, nudge_y = 0, xlim = c(NA, NA), ylim = c(NA, NA), direction = both, seed = NA, verbose = FALSE
#> stat_identity: na.rm = FALSE
#> position_identity

mtcars |>
  tibble::rownames_to_column("car") |>
  filter(wt > 2.75, wt < 3.45) |>
  gg_point(
    x = wt, 
    y = mpg, 
    label = car, 
  ) +
  geom_text_repel() 

ggbeeswarm

The ggbeeswarm package enables beeswarm plots.

library(ggbeeswarm)
geom_beeswarm()
#> geom_point: na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_beeswarm
geom_quasirandom()
#> geom_point: na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_quasirandom

p1 <- penguins |> 
  gg_point(
    position = position_beeswarm(),
    x = sex, 
    y = flipper_length_mm,
    col = sex,
    x_labels = \(x) str_to_upper(str_sub(x, 1, 1)),
    subtitle = "\nBeeswarm",
    mode = light_mode_n(),
  ) 

p2 <- penguins |> 
  gg_point(
    position = ggbeeswarm::position_quasirandom(),
    x = sex, 
    y = flipper_length_mm,
    col = sex,
    x_labels = \(x) str_to_upper(str_sub(x, 1, 1)),
    subtitle = "\nQuasirandom",
    mode = light_mode_n(),
  ) 

p1 + p2

ggridges

The ggridges package enables ridgeline plots.

library(ggridges)
geom_density_ridges()
#> geom_density_ridges: na.rm = FALSE, panel_scaling = TRUE
#> stat_density_ridges: na.rm = FALSE
#> position_points_sina

ggridges::Catalan_elections |>
  rename_with(snakecase::to_snake_case) |>
  mutate(year = factor(year)) |>
  gg_blanket(
    geom = "density_ridges",
    stat = "density_ridges",
    coord = coord_cartesian(),
    x = percent,
    y = year,
    col = option,
    colour = "white",
    x_limits = c(0, 100),
    y_expand = expansion(c(0, 0.125)),
    alpha = 0.9,
  )

ggdist

The ggdist package enables the visualisation of uncertainty.

library(ggdist)
library(distributional)
geom_slabinterval()
#> geom_slabinterval: orientation = NA, normalize = all, fill_type = segments, interval_size_domain = c(1, 6), interval_size_range = c(0.6, 1.4), fatten_point = 1.8, arrow = NULL, show_slab = TRUE, show_point = TRUE, show_interval = TRUE, subguide = none, na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_identity

set.seed(123)

data.frame(
  distribution = c("Gamma(2,1)", "Normal(5,1)", "Mixture"),
  values = c(
    dist_gamma(2, 1),
    dist_normal(5, 1),
    dist_mixture(
      dist_gamma(2, 1),
      dist_normal(5, 1),
      weights = c(0.4, 0.6))
  )) |> 
  gg_blanket(
    geom = "slabinterval",
    stat = "slabinterval",
    y = distribution,
    mapping = aes(dist = values),
    colour = blue,
    fill = blue,
    alpha = 0.6,
    y_expand = c(0.05, 0.05),
    mode = light_mode_n(),
    # show_slab = FALSE,
    # show_interval = FALSE,
    # side = "both",
  )

geom_lineribbon()
#> geom_lineribbon: step = FALSE, orientation = NA, na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_identity

set.seed(123)

tibble(x = 1:10) |> 
  group_by(across(everything()))  |> 
  do(tibble(y = rnorm(100, .$x))) |> 
  median_qi(.width = c(0.5, 0.8, 0.95)) |>
  mutate(.width = factor(.width)) |>
  gg_blanket(
    geom = "lineribbon",
    x = x,
    y = y,
    ymin = .lower,
    ymax = .upper,
    col = .width,
    colour = lightness[1],
    col_legend_rev = TRUE,
    col_palette = blues9[c(7,5,3)],
  )

ggdensity

The ggdensity package provides visualizations of density estimates.

library(ggdensity)
geom_hdr()
#> geom_hdr: na.rm = FALSE
#> stat_hdr: na.rm = FALSE
#> position_identity

set.seed(123)

data.frame(
  x = rnorm(1000),
  y = rnorm(1000),
  z = c(rep("A", times = 500), rep("B", times = 500))
) |>
  gg_blanket(
    geom = "hdr", 
    stat = "hdr",
    x = x, 
    y = y, 
    colour = NA,
    y_limits = c(NA, NA),
  ) +
  guides(colour = FALSE, fill = FALSE)

set.seed(123)

data.frame(
  x = rnorm(1000),
  y = rnorm(1000),
  z = c(rep("A", times = 500), rep("B", times = 500))
) |>
  gg_blanket(
    geom = "hdr", 
    stat = "hdr",
    x = x, 
    y = y, 
    col = z,
    facet = z,
    colour = NA,
    y_limits = c(NA, NA),
  ) 

paletteer

The paletteer package provides access to most of the palettes within R.

library(paletteer)

p1 <- penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
    col = species,
    col_palette = paletteer_d("RColorBrewer::Dark2"),
    x_breaks = scales::breaks_pretty(3),
    mode = light_mode_n(),
  ) 

p2 <- penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
    col = bill_depth_mm,
    col_palette = paletteer_c("viridis::rocket", 9),
    x_breaks = scales::breaks_pretty(3),
    mode = light_mode_n(),
  ) 

p1 + p2

ggblend

The ggblend package provides blending of colours. Note you must use a graphics device that supports blending.

p1 <- penguins |>
  gg_density(
    x = flipper_length_mm,
    col = species,
    y_limits = c(0, NA),
    mode = light_mode_n(),
  ) +
  facet_wrap(~"Normal") +
  ggeasy::easy_remove_x_axis() +
  ggeasy::easy_remove_y_axis()

p2 <- penguins |>
  gg_blanket(
    stat = "density",
    x = flipper_length_mm,
    col = species,
    y_limits = c(0, NA),
    mode = light_mode_n(),
  ) +
  facet_wrap(~"Blended") +
  geom_density(alpha = 0.6) |> ggblend::blend(blend = "multiply") +
  ggeasy::easy_remove_x_axis() +
  ggeasy::easy_remove_y_axis()

p1 + p2

ggh4x

The ggh4x package includes enhanced facet functionality.

iris |> 
  rename_with(\(x) snakecase::to_snake_case(x)) |> 
  mutate(across(species, \(x) str_to_sentence(x))) |> 
  mutate(size = if_else(species == "Setosa", "Short Leaves", "Long Leaves")) |> 
  gg_point(
    x = sepal_width, 
    y = sepal_length, 
    x_breaks = scales::breaks_pretty(n = 3),
  ) +
  ggh4x::facet_nested(
    cols = vars(size, species), 
    nest_line = TRUE, 
    solo_line = TRUE,
  ) +
  theme(strip.text.x = element_text(margin = margin(t = 2.5, b = 5)))

ggeasy

The ggeasy package provides a lot of support for easily modifying themes. Removing axes and gridlines is often useful.

library(ggeasy)

penguins |>
  mutate(across(sex, str_to_sentence)) |> 
  gg_jitter(
    x = species, 
    y = body_mass_g, 
    col = sex, 
  ) +
  light_mode_t() +
  easy_remove_y_gridlines() +
  easy_remove_x_axis(what = c("line", "ticks")) +
  easy_remove_legend_title()

ggnewscale

The ggnewscale package enables multiple colour scales.

penguins |> 
  gg_blanket(
    x = flipper_length_mm, 
    y = body_mass_g,
  ) + 
  geom_point(
    mapping = aes(colour = bill_length_mm), 
    data = penguins |> filter(species == "Gentoo"),
  ) +
  scale_color_viridis_c(option = "G", direction = -1) +
  labs(colour = "Gentoo\nBill length (mm)") +
  ggnewscale::new_scale_colour() +
  geom_point(
    mapping = aes(colour = species), 
    data = penguins |> filter(species != "Gentoo"),
  ) +
  scale_color_manual(values = c("#901752", orange)) +
  labs(colour = "Species")