Assign different background color for each legend in ggplot2

This page shows how to manipulate the multiple legends of a ggplot such as order, color of title using the guide_legend function. I wonder if it is possible to modify the background color of each legend individually. Thanks!

ggplot(mpg, aes(displ, cty)) +   # I tired to use legend.background and a list of colors in fill to individually change the color, but is it not working   theme(legend.background=element_rect(fill=c('brown','grey','whie'))) +   geom_point(aes(size = hwy, colour = cyl, shape = drv)) +   guides(     colour = guide_colourbar(order = 1),     # title.theme allows individual adjustment of title color, I wonder if similar can be done for legend background color     shape = guide_legend(order = 2,title.theme = element_text(color='green')),     size = guide_legend(order = 3,label.theme=element_text(color='red'))   ) 
Add Comment
4 Answer(s)

I don’t think there’s a way to send multiple colors to legend.background. If this is really important to you, then you probably need to hack the grobs in the final plot. Here’s a little function that can do it without using any external packages:

recolor_legends <- function(gg_plot, col) {   p2      <- ggplotGrob(gg_plot)   grobs   <- p2$grobs[which(p2$layout$name == "guide-box")][[1]]$grobs   legends <- grobs[sapply(grobs, function(x) any(grepl("grobs", names(x))))]   bgs     <- lapply(legends, function(x) {     x$grobs[x$layout$name == "background"][[1]]   })   bgs <- mapply(function(x, y) {x$gp$fill <- y; x}, bgs, col, SIMPLIFY = FALSE)   legends <- mapply(function(x, y){     x$grobs[x$layout$name == "background"][[1]] <- y; x   }, legends, bgs, SIMPLIFY = FALSE)   grobs[sapply(grobs, function(x) any(grepl("grobs", names(x))))] <- legends   p2$grobs[which(p2$layout$name == "guide-box")][[1]]$grobs <- grobs   plot(p2) } 

So suppose I have the following plot:

p <- ggplot(mpg, aes(displ, cty)) +   geom_point(aes(size = hwy, colour = cyl, shape = drv)) +   guides(     colour = guide_colourbar(order = 1),     shape = guide_legend(order = 2,     title.theme = element_text(color = 'green')),     size = guide_legend(order = 3, label.theme = element_text(color = 'red'))   )  p 

enter image description here

I can just do

recolor_legends(p, c("red", "blue", "green")) 

enter image description here

Add Comment

This is one way of doing it, but it’s a bit convoluted (based on this excellent post):

# Some data df <- data.frame(   x = 1:10,   y = 1:10,   colour = factor(sample(1:3, 10, replace = TRUE)),   size = factor(sample(1:3, 10, replace = TRUE)))  library(ggplot2) library(gridExtra) library(gtable) library(grid)  ### Step 1 # Draw a plot with the colour legend (p1 <- ggplot(data = df, aes(x=x, y=y)) +     geom_point(aes(colour = colour)) +     theme_bw() +     theme(legend.position = "top", legend.background = element_rect(fill = "lightsteelblue")))  # Extract the colour legend - leg1 leg1 <- gtable_filter(ggplot_gtable(ggplot_build(p1)), "guide-box")  ### Step 2 # Draw a plot with the shape legend (p2 <- ggplot(data = df, aes(x=x, y=y)) +     geom_point(aes(shape = size)) +     theme_bw() +     theme(legend.background = element_rect(fill = "lightseagreen")))  # Extract the shape legend - leg2 leg2 <- gtable_filter(ggplot_gtable(ggplot_build(p2)), "guide-box")  # Step 3 # Draw a plot with no legends - plot (plot <- ggplot(data = df, aes(x=x, y=y)) +     geom_point(aes(shape = size, colour = colour)) +     theme_bw() +     theme(legend.position = "none"))  ### Step 4 # Arrange the three components (plot, leg1, leg2) # The two legends are positioned outside the plot:  # one at the top and the other to the side. plotNew <- arrangeGrob(leg1, plot,                         heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1)  plotNew <- arrangeGrob(plotNew, leg2,                        widths = unit.c(unit(1, "npc") - leg2$width, leg2$width), nrow = 1)  grid.newpage() grid.draw(plotNew)  # OR, arrange one legend at the top and the other inside the plot. plotNew <- plot +    annotation_custom(grob = leg2, xmin = 7, xmax = 10, ymin = 0, ymax = 4)  plotNew <- arrangeGrob(leg1, plotNew,                        heights = unit.c(leg1$height, unit(1, "npc") -  leg1$height), ncol = 1)  grid.newpage() grid.draw(plotNew) 
Answered on August 30, 2020.
Add Comment

You can also do this using lower level grid function to edit the relevant grobs. If p is the plot in your question:

library(grid)  # allow the ggplot grobs to be seen by `grid` package functions g = grid.force(ggplotGrob(p))  # grab the names of the guide grobs, to be used in gPath in `editGrob` nms = grep("^guides", grid.ls(g, print=FALSE)$name, value=TRUE)  # edit the guide grobs by passing a vector of colours to `gpar` for(i in seq_along(nms))    g = editGrob(g, gPath("guide-box", nms[[i]], "background"),                 grep=TRUE,                 gp = gpar(fill=c("red", "blue", "green")[i]))  grid.newpage(); grid.draw(g) 

Which gives

enter image description here

Add Comment

Based on the answer from Allan, I have enhanced the function so that background color of the keys are now transparent.

ggplot.legends.recolor.fx <- function(gg_plot, col) {   p2      <- ggplotGrob(gg_plot)   grobs   <- p2$grobs[which(p2$layout$name == "guide-box")][[1]]$grobs   legends <- grobs[sapply(grobs, function(x) any(grepl("grobs", names(x))))]   bgs     <- lapply(legends, function(x) {     x$grobs[x$layout$name == "background"][[1]]   })   bgs <- mapply(function(x, y) {x$gp$fill <- y; x}, bgs, col, SIMPLIFY = FALSE)   bgs <- mapply(function(x, y) {x$gp$alpha <- y; x}, bgs, 0.3, SIMPLIFY = FALSE)   legends <- mapply(function(x, y){     x$grobs[x$layout$name == "background"][[1]] <- y; x   }, legends, bgs, SIMPLIFY = FALSE)      # background color for the keys are changed to transparent below:   key.bgs <- lapply(legends, function(x) {     x$grobs[grepl('key.*bg',x$layout$name)]   })       key.bgs <- lapply(key.bgs,function(z) {     z <- mapply(function(x, y) {x$gp$alpha <- y; x}, z, 0, SIMPLIFY = FALSE)})   legends <- mapply(function(x, y){     x$grobs[grepl('key.*bg',x$layout$name)] <- y; x   }, legends, key.bgs, SIMPLIFY = FALSE)      grobs[sapply(grobs, function(x) any(grepl("grobs", names(x))))] <- legends      p2$grobs[which(p2$layout$name == "guide-box")][[1]]$grobs <- grobs   plot(p2) } 
Answered on August 30, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.