1

I'm using faceting to break a graph showing multiple groups of data into manageable pieces so that each facet panel presents a small number of groups for comparison. I'm plotting unequal numbers of groups per facet, so I'm wondering if the geometry of the legend can be adjusted to reflect the number of groups in each facet.

I know how to adjust the number of rows or columns in the legend (e.g. using guides(fill=guide_legend(nrow=2)) to set the legend to having 2 rows as in the answer https://stackoverflow.com/a/27131447/2296603). Can the legend layout be adjusted in a complex manner, setting the number of rows for each column separately?

For this example below, I would like to have the 1st and 3rd columns use 3 rows, and the 2nd column have 2 rows, like so:

A C
ABC
ABC

Instead, ggplot2 by default 'reflows' the legend items so they are instead formatted like this:

ABC
ABC
AC

Below is an example plot with the legend reflowed over 3 rows:

library(tidyverse)

example_data <- tibble(
  group = factor(rep(letters[1:8], each = 5)),
  facet_group = rep(c(1,2,3), times = c(15, 10, 15)),
  x = rep(1:5, times = 8),
  y = round(runif(40) * 100)
)

ggplot(
  data = example_data,
  aes(x = x, y = y, shape = group, colour = group)
) +
  geom_point() +
  geom_line() +
  facet_wrap(~ facet_group) +
  guides(shape = guide_legend(nrow = 3))

This is the resulting plot, with the 'reflowed' legend layout:

enter image description here

jsavn
  • 701
  • 1
  • 8
  • 17

1 Answers1

3

I'm not aware of an easy way to do this. I can see a hard way to do it, which is to create an empty 'spacer' in the legend by inserting an unused factor level and overriding the aesthetics of the guide:

example_data %>%
  mutate(group = factor(group, levels = c('a', 'b', 'c', 'd', '', 'e', 
                                          'f', 'g', 'h'))) %>%
  ggplot(aes(x = x, y = y, shape = group, colour = group)) +
  geom_point() +
  geom_line() +
  scale_color_discrete(breaks = c('a', 'b', 'c', '', 'd', 'e', 
                                  'f', 'g', 'h'), drop = FALSE) +
  scale_shape_manual(values = c(1:5, NA, 6:8),
                     breaks = c('a', 'b', 'c', '', 'd', 'e', 
                                'f', 'g', 'h'), drop = FALSE) +
  facet_wrap(~ facet_group)  +
  guides(color = guide_legend(nrow = 3, 
                              override.aes = list(
          linetype = c(rep(1, 3), 0, rep(1, 5)),
          shape = c(1:3, NA, 4:8)))) +
  theme(legend.key = element_rect(fill = NA),
        legend.title = element_text(hjust = 0.5))

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • This is a great suggestion, thanks! Will it generalise to multiple 'spacer' levels, though? I.e. if I increased the number of columns and several needed one or more 'spacer' levels? – jsavn Mar 13 '23 at 15:26
  • @jsavn I guess it would. You would need multiple levels in your factor called eg 'spacer1', 'spacer2' etc, and use a custom labelling function in the scale_color_discrete and scale_shape_discrete like `labels = ~ifelse(grepl('spacer', .x), '', .x)` – Allan Cameron Mar 13 '23 at 15:30
  • 1
    @jsavn although, to be honest, if you have to take special steps to construct a complex legend, your plot is probably going to be too complex for people to easily take in. In these scenarios, it is usually better to have a series of individual plots rather than a single big faceted plot with indistinguishably many colors and shapes. From a data viz point of view, your plot is already a bit too complex to convey information to the audience. – Allan Cameron Mar 13 '23 at 15:33
  • Right, so the goal is to create spacer levels where needed to fill a rectangular grid, and replace their values for the relevant aesthetics to `NA` and the labels to `''` (or `NA_character_`, whichever one works for making a blank label) – jsavn Mar 13 '23 at 15:34
  • Agreed re: visualization complexity - I'm using this to group multiple levels that are quite similar within each group, and to keep the legend labels grouped accordingly - the goal is more of a proof of concept than to have a simple to read graph, which I agree individual plots would be better suited for. – jsavn Mar 13 '23 at 15:37