Search code examples
rggplot2tidyverse

Complex legend layout with ggplot2


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


Solution

  • 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