Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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'))
  )
like image 746
lokheart Avatar asked Aug 25 '20 10:08

lokheart


2 Answers

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)
like image 38
jared_mamrot Avatar answered Sep 22 '22 12:09

jared_mamrot


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

like image 61
Allan Cameron Avatar answered Sep 24 '22 12:09

Allan Cameron