ggblanket

David Hodge

Overview

To simplify ggplot2 visualisation, ggblanket provides:

  1. Over thirty gg_* wrapper functions
  2. A single col argument to colour and fill by a variable
  3. A pal argument to customise colours
  4. A facet argument to facet by a variable
  5. An additional facet2 argument to facet by a 2nd variable
  6. Prefixed arguments to customise titles, scales, guides, and faceting
  7. Unspecified x/y/col titles converted to sentence case
  8. Ability to set the theme, including to provided themes
  9. Pretty defaults for symmetry
  10. Access to other geom_* arguments via …
  11. Ability to add multiple geom_* layers
  12. A framework able to be extended further
library(dplyr)
library(ggplot2)
library(ggblanket)
library(patchwork)

penguins <- palmerpenguins::penguins |>
  mutate(sex = stringr::str_to_sentence(sex)) |>
  tidyr::drop_na(sex)

1. Over thirty gg_* wrapper functions

Each gg_* function wraps a ggplot2 ggplot(aes(...)) function with the applicable ggplot2 geom_*() function. All aesthetics are placed directly in the gg_* function: they are not within a ggplot2::aes function. This also provides access to the arguments of the relevant stat function.

# ggplot2
penguins |> 
  ggplot() + 
  geom_point(aes(x = flipper_length_mm, 
                 y = body_mass_g))

# ggblanket
penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g)

2. A single col argument to colour and fill by a variable

The colour and fill aesthetics of ggplot2 are merged into a single concept represented by the col argument. This argument means that everything should be coloured according to it, i.e. all points, lines and polygon interiors.

# ggplot2
p1 <- penguins |> 
  ggplot() + 
  geom_point(aes(x = flipper_length_mm, 
                 y = body_mass_g,
                 col = species)) + 
  theme(legend.position = "bottom") +
  guides(col = guide_legend(title.position = "top")) +
  labs(col = "Species") +
  scale_x_continuous(breaks = scales::breaks_pretty(n = 3))

p2 <- penguins |>
  ggplot() +
  geom_density(aes(x = body_mass_g, 
                   fill = species)) +
  theme(legend.position = "bottom") +
  guides(fill = guide_legend(title.position = "top")) +
  labs(fill = "Species")

p1 + p2

# ggblanket
p1 <- penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g, 
    col = species, 
    x_breaks = scales::breaks_pretty(n = 3), 
    col_legend_ncol = 2)

p2 <- penguins |>
  gg_density(
    x = body_mass_g, 
    col = species,
    x_breaks = scales::breaks_pretty(n = 3), 
    col_legend_ncol = 2)

p1 + p2

3. A pal argument to customise colours

The pal argument is used to customise the colours of the geom. A user can provide a vector of colours to this argument. It can be named or not. It works in a consistent way - regardless of whether a col argument is added or not. A named palette can be used to make individual colours stick to particular values.

# ggplot2
p1 <- penguins |>
  ggplot() +
  geom_histogram(aes(x = body_mass_g),
                 fill = "#1B9E77") +
  scale_x_continuous(breaks = scales::breaks_pretty(n = 3)) 

p2 <- penguins |>
  ggplot() +
  geom_jitter(aes(x = species, 
                  y = body_mass_g, 
                  col = sex)) +
  scale_color_manual(values = c("#2596be", "#fc7c24"))

p1 + p2

# ggblanket
p1 <- penguins |>
  gg_histogram(
    x = body_mass_g, 
    pal = "#1B9E77", 
    x_breaks = scales::breaks_pretty(n = 3))

p2 <- penguins |>
  gg_jitter(
    x = species, 
    y = body_mass_g, 
    col = sex, 
    pal = c("#2596be", "#fc7c24"))

p1 + p2

4. A facet argument to facet by a variable

Faceting is treated as if it were an aesthetic. Users just provide an unquoted variable to facet by. If a single facet (or facet2) variable is provided, it’ll default to a “wrap” layout. But users can change this with a facet_layout = "grid" argument.

# ggplot2
penguins |>
  ggplot() +
  geom_violin(aes(x = sex, 
                  y = body_mass_g)) +
  facet_wrap(~species) 

# ggblanket
penguins |>
  gg_violin(
    x = sex,
    y = body_mass_g,
    facet = species)

5. An additional facet2 argument to facet by a 2nd variable

A facet2 argument is also provided for extra functionality and flexibility. If both facetand facet2 variables are provided, then it’ll default to a “grid” layout of facet by facet2. But users can change this with a facet_layout = "wrap" argument.

# ggplot2
penguins |>
  ggplot() +
  geom_histogram(aes(x = flipper_length_mm)) +
  facet_grid(species ~ sex)

# ggblanket
penguins |>
  gg_histogram(
    x = flipper_length_mm,
    facet = sex,
    facet2 = species)

6. Prefixed arguments to customise titles, scales, guides, and faceting

Prefixed arguments are available to customise titles, scales, guides, and faceting. These are designed to work with the Rstudio auto-complete to help users remember and find the adjustment they need. Users should first determine whether they want to change something that relates to x, y, col or facet. Then they should type this prefix and press the tab key to access the list of options from the Rstudio auto-complete. Then they can use the arrow keys, and press tab again to select what they want.

# ggplot2
penguins |>
  ggplot() +
  geom_jitter(aes(x = species, 
                  y = body_mass_g, 
                  col = sex)) +
  expand_limits(y = 0) +
  scale_x_discrete(labels = \(x) stringr::str_sub(x, 1, 1)) +
  scale_y_continuous(breaks = scales::breaks_width(1500),
                     labels = scales::label_number(big.mark = " "),
                     trans = "sqrt") +
  labs(x = "Species", y = "Body mass (g)", col = "Sex") +
  theme(legend.position = "top") +
  theme(legend.justification = "left") +
  scale_colour_manual(values = scales::hue_pal()(2), 
                      guide = ggplot2::guide_legend(title.position = "top"))

# ggblanket
penguins |>
  gg_jitter(
    x = species,
    y = body_mass_g,
    col = sex,
    x_labels = \(x) stringr::str_sub(x, 1, 1),
    y_include = 0,
    y_breaks = scales::breaks_width(1500), 
    y_labels = scales::label_number(big.mark = " "), 
    y_trans = "sqrt",
    y_title = "Body mass (g)", 
    col_legend_place = "t")

7. Unspecified x/y/col titles converted to sentence case

Unspecified x/y/col titles are converted to sentence case with snakecase::to_sentence. All titles can be manually changed using the *_title arguments. The default conversion is intended to make titles sometimes able to be left as is.

# ggplot2
penguins |>
  ggplot() +
  geom_point(aes(x = flipper_length_mm, 
                 y = body_mass_g, 
                 col = sex)) +
  facet_wrap(~species) +
  scale_x_continuous(breaks = scales::breaks_pretty(n = 3)) 

# ggblanket
penguins |>
  gg_point(
      x = flipper_length_mm,
      y = body_mass_g, 
      col = sex,
      facet = species)

8. Ability to set the theme, including to provided themes

The ggplot2::theme_set function can be used to set the theme globally, such that all subsequent plots will use this where the theme argument of a plot is left as NULL. Alternatively, users can use the theme argument in a gg_* function.

ggblanket provides two complete ggplot2 theme functions called light_mode (the default) and dark_mode. The first argument is the base_size. This changes the size of all the text to this, except the title is 10% higher and the caption is 10% lower. There are other arguments to change the family, face, colour, size, margins and vertical adjustment of text.

Note that the gg_* function will control what gridlines are present and the placement of the legend - despite the theme provided. Therefore, if you are providing a theme other than light_mode or dark_mode, ggblanket works well if this theme has both vertical and horizontal gridlines. If users want everything adjusted as per the theme, then they can + their theme onto the plot instead.

Note that theme_set(theme_grey()) resets the set theme for ggplot2 code to theme_grey and for ggblanket gg_* functions to light_mode(). If you want ggblanket gg_* functions to default to using theme_grey(), then you must modify the base_size slightly (e.g. theme_set(theme_grey(11.01))).

# ggblanket
theme_set(dark_mode())

penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
    col = sex,
    title = "Penguins body mass by flipper length",
    subtitle = "Palmer Archipelago, Antarctica",
    caption = "Source: Gorman, 2020",
    pal = c("#2596be", "#fc7c24"))

# ggblanket
theme_set(light_mode(base_size = 12))

penguins |>
  gg_point(
    x = flipper_length_mm,
    y = body_mass_g,
    col = sex,
    title = "Penguins body mass by flipper length",
    subtitle = "Palmer Archipelago, Antarctica",
    caption = "Source: Gorman, 2020",
    pal = c("#2596be", "#fc7c24"))

theme_set(theme_grey()) #unset the theme

9. Pretty defaults for symmetry

Where the orientation is normal (i.e. vertical):

It does the opposite where the orientation is horizontal (and also keeps y categorical values in correct order).

Note gg_raster deviates froms this approach, and instead places the limits as the min and max of the data by default.

# ggplot2
penguins |>
  group_by(species, sex) |> 
  summarise(body_mass_g = mean(body_mass_g)) |> 
  ggplot() +
  geom_col(aes(x = body_mass_g, 
               y = species, 
               fill = sex),
           position = "dodge",
           width = 0.75)

# ggblanket
penguins |>
  group_by(species, sex) |> 
  summarise(body_mass_g = mean(body_mass_g)) |> 
  gg_col(
    x = body_mass_g,
    y = species,
    col = sex,
    position = "dodge",
    width = 0.75)

10. Access to other geom_* arguments via ...

The ... argument is placed in the gg_* function within the wrapped ggplot2::geom_* function. This means all other arguments in the geom_* function are available to users (except the mapping argument). Common arguments from ... to add are size, linewidth and width.

# ggblanket
penguins |>
  gg_smooth(
    x = flipper_length_mm,
    y = body_mass_g,
    col = sex,
    linewidth = 0.5, #accessed via geom_smooth
    level = 0.99) #accessed via geom_smooth

11. Ability to add multiple geom_* layers

Users can make plots with multiple layers with ggblanket by adding on ggplot2::geom_* layers. The gg_* function puts the aesthetics within the wrapped ggplot function. Therefore, the aesthetics will inherit to any subsequent geoms added. Geoms will plot in order. The gg_* function will plot the associated geom as the first layer, and then other geoms will be plotted on top of it.

# ggblanket + ggplot2
penguins |>
  gg_boxplot(x = species,
             y = body_mass_g,
             width = 0.5,
             outlier.colour = NA) +
  geom_jitter(col = pal_blue)

If some geoms have a col aesthetic and some do not, then it is recommended to have the col aesthetic in the gg_* function. This enables ggblanket legend placement, as well as the ability to adjust the col scale via the gg_* function.

If you do not want a col aesthetic in your bottom plot layer, but require it in subsequent layers - then you should use the gg_blank function. Subsequent geom_* layers where the col aesthetic is not required can have col = NA and/or fill = NA arguments - or use the inherit.aes = FALSE argument.

Note the plot scales are constructed without knowledge of subsequent layers. The gg_* function builds the x and y scales based on the data, stat, x, and y in the gg_* function. These scales can be adjusted to accommodate subsequent layers using the *_include or *_limits arguments.

# ggblanket + ggplot2
d <- penguins |>
  group_by(species) |>
  summarise(body_mass_g = mean(body_mass_g)) |>
  mutate(lower = body_mass_g * 0.95) |> 
  mutate(upper = body_mass_g * 1.2)

p1 <- d |>
  gg_blank(    
    y = species,
    x = body_mass_g,
    col = species,
    xmin = lower,
    xmax = upper,
    x_include = 0, 
    x_labels = \(x) x / 1000, 
    x_title = "Body mass kg") +
  geom_col(alpha = 0.9, width = 0.75) +
  geom_errorbar(col = "black", width = 0.1)

p2 <- d |>
  gg_blank(
    y = species,
    x = body_mass_g,
    xmin = lower, 
    xmax = upper, 
    col = species,
    x_include = 0, 
    x_labels = \(x) x / 1000, 
    x_title = "Body mass kg") +
  geom_col(col = NA, fill = "#d3d3d3", width = 0.75) +
  geom_errorbar(width = 0.1)

p1 / p2

# ggblanket + ggplot2
d_wide <- gapminder::gapminder |>
  filter(year %in% c(1967, 2007)) |>
  select(country, year, lifeExp) |>
  tidyr::pivot_wider(names_from = year, values_from = lifeExp) |>
  mutate(gap = `2007` - `1967`) |>
  slice_max(gap, n = 10) |>
  mutate(country = forcats::fct_inorder(forcats::fct_drop(country))) 

d_long <- d_wide |>
  select(-gap) |> 
  tidyr::pivot_longer(-country, 
                      names_to = "year", 
                      values_to = "life_expectancy")

d_long |> 
  gg_blank(x = life_expectancy,
           y = country,
           col = year,
           pal = pal_hue[c(2, 1)],
           x_include = 0,
           col_legend_place = "r",
           title = "We're living longer",
           subtitle = "Biggest life expectancy rise, 1967\u20132007",
           x_title = "Life expectancy", 
           y_title = "") +
  geom_segment(aes(x = `1967`, xend = `2007`, 
                   y = country, yend = country), 
               data = d_wide, inherit.aes = FALSE, 
               col = "#dddddd", linewidth = 2) +
  geom_point(size = 2) 

12. A framework able to be extended further

To extend ggblanket further, users can:

See the Extending further article for further information.