DidDrog11
DidDrog11

Reputation: 61

How to group a legend or get seperate legends by facets in ggplot2

I have a dataset that I am presenting facetted by region and then using sub region as a fill. I have defined the colours using a separate named variable relating to the names of the subregion. I am wondering if it is possible to make the legend itself grouped in a similar way to the facet to make it easier to interpret.

The named sub_region variable

sub_region_colours <- c("South America" = "#0570b0", "Western Africa" = "#8c96c6", "Central America" = "#74a9cf", "Eastern Africa" = "#8856a7", "Northern Africa" = "#edf8fb", "Middle Africa" = "#b3cde3", "Southern Africa" = "#810f7c", "Northern America" = "#f1eef6", "Caribbean" = "#bdc9e1", "Eastern Asia" = "#bd0026", "Southern Asia" = "#fd8d3c", "South-Eastern Asia" = "#f03b20", "Southern Europe" = "#238b45", "Australia and New Zealand" = "#ce1256", "Melanesia" = "#df65b0", "Micronesia" = "#d7b5d8", "Polynesia" = "#f1eef6", "Central Asia" = "#fecc5c", "Western Asia" = "#ffffb2", "Eastern Europe" = "#66c2a4", "Northern Europe" = "#edf8fb", "Western Europe" = "#b2e2e2", "Small Islands" = "#252525")

This is the head(exporting_countries) grouping by sender_iso3, year and sender_region removed.

structure(list(sender_iso3 = c("ABW", "ABW", "ABW", "ABW", "ABW", 
"ABW"), year = c(2005, 2011, 2014, 2015, 2016, 2017), sender_region = c("Americas", 
"Americas", "Americas", "Americas", "Americas", "Americas"), 
    sender_subregion = c("Caribbean", "Caribbean", "Caribbean", 
    "Caribbean", "Caribbean", "Caribbean"), export = c(1, 1, 
    4, 5, 2, 1)), class = "data.frame", row.names = c(NA, -6L
))

Finally this is the code for the current plot

  geom_bar()+
  labs(title = "Number of countries reporting export of chickens",
       fill = "Subregion")+
  facet_wrap(~ sender_region)+
  theme_minimal()+
  scale_x_continuous(name = "Year", limits = c(1986, 2017), breaks = c(1986, 1990, 2000, 2010, 2017), guide = guide_axis(angle = 90))+
  scale_fill_manual(values = sub_region_colours)+
  guides(fill = guide_legend(ncol = 2))

Which at the moment produces this:

Graph with less than ideal legend

It would be great if I can group the legend fill colours similarly to the facets which would make it easier to read off.

Upvotes: 1

Views: 885

Answers (1)

stefan
stefan

Reputation: 123903

One approach to achieve this would be to make seperate plots for each region and make use of patchwork to glue the plots together. A second approach would be to make use of the ggnewscale package which allows to have multiple fill (or ...) scales and legends in one plot.

However, similiar to using patchwork the approach using ggnewscale package could become a bit tedious as it requires to split the data according to the number of facets and plot each dataset via seperate layers. Therefore my solution adds a helper function which 1) splits the data and sets up the layers for each region or facet and 2) can be used to loop over the regions via e.g. lapply.

BTW: As your sample data included only one region I added a second region.

library(dplyr)
library(ggplot2)
library(ggnewscale)

sub_region_colours <- c("South America" = "#0570b0", "Western Africa" = "#8c96c6", "Central America" = "#74a9cf", "Eastern Africa" = "#8856a7", "Northern Africa" = "#edf8fb", "Middle Africa" = "#b3cde3", "Southern Africa" = "#810f7c", "Northern America" = "#f1eef6", "Caribbean" = "#bdc9e1", "Eastern Asia" = "#bd0026", "Southern Asia" = "#fd8d3c", "South-Eastern Asia" = "#f03b20", "Southern Europe" = "#238b45", "Australia and New Zealand" = "#ce1256", "Melanesia" = "#df65b0", "Micronesia" = "#d7b5d8", "Polynesia" = "#f1eef6", "Central Asia" = "#fecc5c", "Western Asia" = "#ffffb2", "Eastern Europe" = "#66c2a4", "Northern Europe" = "#edf8fb", "Western Europe" = "#b2e2e2", "Small Islands" = "#252525")

d <- structure(list(sender_iso3 = c(
  "ABW", "ABW", "ABW", "ABW", "ABW",
  "ABW", "ABW", "ABW", "ABW", "ABW", "ABW", "ABW"
), year = c(
  2005,
  2011, 2014, 2015, 2016, 2017, 2005, 2011, 2014, 2015, 2016, 2017
), sender_region = c(
  "Americas", "Americas", "Americas", "Americas",
  "Americas", "Americas", "Africa", "Africa", "Africa", "Africa",
  "Africa", "Africa"
), sender_subregion = c(
  "Caribbean", "Caribbean",
  "Caribbean", "Caribbean", "Caribbean", "Caribbean", "Southern Africa",
  "Southern Africa", "Southern Africa", "Southern Africa", "Southern Africa",
  "Southern Africa"
), export = c(
  1, 1, 4, 5, 2, 1, 1, 1, 4, 5,
  2, 1
)), class = "data.frame", row.names = c(NA, -12L))
regions <- unique(d$sender_region)

# Layers for each region
make_layers <- function(x) {
  d <- filter(d, sender_region == regions[[x]])

  list(
    if (x != 1) new_scale_fill(),
    geom_bar(data = d, aes(x = year, fill = sender_subregion)),
    scale_fill_manual(
      values = sub_region_colours,
      guide = guide_legend(
        order = x,
        title = regions[x],
        title.position = "top"
      )
    )
  )
}
p <- ggplot() +
  lapply(seq_along(regions), make_layers)

# Add theme and wrap
p +
  theme_minimal() +
  scale_x_continuous(
    name = "Year", limits = c(1986, 2017),
    breaks = c(1986, 1990, 2000, 2010, 2017),
    guide = guide_axis(angle = 90)
  ) +
  facet_wrap(~sender_region)

Upvotes: 1

Related Questions