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")
