7

I'm attempting to get the text color of legend labels to match their associated fill/line colors. Ideally, I'd like to map the label color to an aesthetic, but at this point I'd be just as happy to manually specify the colors. Here is some test code using the built-in PlantGrowth dataset, which includes my attempt to manually specify the label colors:

ggplot(data=PlantGrowth, aes(x=group, y=weight, fill=group)) +
    geom_boxplot() +
    scale_fill_discrete(guide=guide_legend(label.theme=element_text(angle=0,
                                                                    size=9,
                                                                    color=c("red",
                                                                            "green",
                                                                            "blue"))))

When I run this code the legend labels all use the first color I specify (red). Instead, I want each legend label to use a different color. Is this type of operation currently possible in ggplot2?

Brain_Food
  • 718
  • 1
  • 7
  • 17

1 Answers1

6

First off, I tried experimenting with different versions of your current attempt as well as using theme(legend.text = element_text(color = c("red","blue","green"))) but none of the above worked so I had to go to gtables.

What you want is possible, but requires you to be very familiar with gtables and the gtable package. This is going to look very messy because there are a lot of nested lists and the object we ultimately want to modify is at the bottom of one. Just bear with me:

library(ggplot2)
library(gtable)
library(grid)
pGrob <- ggplotGrob(ggplot(data=PlantGrowth, aes(x=group, y=weight, fill=group)) +
  geom_boxplot() +
  scale_fill_discrete(guide=guide_legend(label.theme=element_text(angle=0,
                                                                  size=9))))



gb <- which(grepl("guide-box", pGrob$layout$name))
gb2 <- which(grepl("guides", pGrob$grobs[[gb]]$layout$name))
label_text <- which(grepl("label",pGrob$grobs[[gb]]$grobs[[gb2]]$layout$name))
pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text] <- mapply(FUN = function(x, y) {x$gp$col <- y; return(x)},
                                                           x =   pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text],
                                                           y =   c("red", "green", "blue"), SIMPLIFY = FALSE)

grid.draw(pGrob)

enter image description here

The first 3 lines that create gb, gb2, and label_text are all designed to dynamically get to that bottom level we want to modify. Once we have our path to get the objects, we can then modify them using the mapply and change the col to be the vector you want.

Mike H.
  • 13,960
  • 2
  • 29
  • 39
  • 1
    Great answer! Thanks for this. I will just add that if you have multiple legends and only want to apply this change to one of them, I think you'll need to be a little careful with how you assign a value to gb2. Since all of the legends will have the "guides" name, you need to check which one you want to edit and set gb2 accordingly. That is less automated and requires some more hands-on work. – Brain_Food Apr 18 '17 at 19:22