1

Is there a way of arranging the ggplot legend by groups?

If I try:

library(tidyverse)
mtcars_tbl <- mtcars %>% 
  rownames_to_column(var = "make_model") %>%
  as_tibble %>% 
  mutate(point_col = case_when(cyl == 4 ~ "#009E73",
                               cyl == 6 ~ "#0072B2",
                               cyl == 8 ~ "#D55E00"))
cols_tbl <- mtcars_tbl %>% 
  select(make_model, point_col) %>% 
  deframe

mtcars_tbl %>%
  ggplot(aes(x = hp, y = qsec, col = make_model)) +
  geom_point() +
  scale_colour_manual(values = cols_tbl) +
  theme(legend.position = "bottom")

The make_model comes out arranged alphabetically. I can specify the number of rows or columns or even change make_model to factor but what I'd like is the way they are arranged in the picture below (importantly I want to have an uneven number of items).

enter image description here

biomiha
  • 1,358
  • 2
  • 12
  • 25
  • That's effecvtively a multi-variate aesthetic grouping, which I would love to see but have run into similar road-blocks. There has been some work in [`ggh4x`](https://github.com/teunbrand/ggh4x), perhaps that's a start for you. – r2evans Aug 03 '21 at 17:47

1 Answers1

1

One option to achieve your desired result would be to make use of the ggnewscale package which allows for multiple color scales. One drawback of this approach is that it requires plotting your data via multiple geom layers too. In my approach below I first split the data as well as the color vectors by groups. To get the order of the groups right it's best make use of the order argument of guide_legend. Additionally I make use of the title.theme argument to "remove" the duplicated legend titles. Finally I use theme options to align the guides vertically and to the left.

library(tidyverse)
library(ggnewscale)

mtcars_tbl <- mtcars %>% 
  rownames_to_column(var = "make_model") %>%
  as_tibble %>% 
  mutate(point_col = case_when(cyl == 4 ~ "#009E73",
                               cyl == 6 ~ "#0072B2",
                               cyl == 8 ~ "#D55E00"))
cols_tbl <- mtcars_tbl %>% 
  select(make_model, point_col) %>% 
  deframe()

mtcars_tbl <- split(mtcars_tbl, mtcars_tbl$cyl)
cols_tbl <- lapply(mtcars_tbl, function(x) x %>% select(make_model, point_col) %>% deframe())

ggplot(mapping = aes(x = hp, y = qsec)) +
  geom_point(data = mtcars_tbl$`4`, aes(col = make_model)) +
  scale_colour_manual(values = cols_tbl$`4`, 
                      guide = guide_legend(order = 1, title.theme = element_text(color = NA))) +
  new_scale_color() +
  geom_point(data = mtcars_tbl$`6`, aes(col = make_model)) +
  scale_colour_manual(values = cols_tbl$`6`,
                      guide = guide_legend(order = 2, title.theme = element_text(color = "black"))) +
  new_scale_color()+
  geom_point(data = mtcars_tbl$`8`, aes(col = make_model)) +
  scale_colour_manual(values = cols_tbl$`8`,
                      guide = guide_legend(order = 3, title.theme = element_text(color = NA))) +
  theme(legend.position = "bottom", 
        legend.box = "vertical",
        legend.box.just = "left")

stefan
  • 90,330
  • 6
  • 25
  • 51
  • This is excellent and exactly solved the problem I was looking for. Out of curiosity, is there a good way to add a "main" legend title, and then have titles for each of the new_scale_color() sections? In the above example, that would be a main title of "Engine Cylinders" and then subtitles for 4, 6, and 8. – Alex Krohn Jun 27 '22 at 16:02
  • 1
    Hi @AlexKrohn. AFAIK there is no easy option to add a main title while having section titles too. In general you have to add the main title as some kind of annotation. To this end see e.g. [Add total number to ggplot legend](https://stackoverflow.com/questions/72556876/add-total-number-to-ggplot-legend/72557229#72557229) for some options including an answer by me which makes use of `cowplot` and `patchwork` which would be my way to do it. – stefan Jun 27 '22 at 16:43