2

I am interested in automatically extracting the legend of ggplot2 object as a table (not as a graphics object). Let's illustrate with an example:

p <- ggplot(iris, aes(x=Sepal.Length, y= Petal.Length, color=Species)) +
       geom_point()
p

iris example

Now there is a previous question, which shows how one can extract the colours used in the plot:

 g <- ggplot_build(p)
 unique(g$data[[1]]["colour"])

      colour
 1   #F8766D
 51  #00BA38
 101 #619CFF

But I am also interested in the labels which those colours correspond to in the legend, i.e. the final result I would be interested in is the following table:

colour    label
#F8766D   setosa
#00BA38   versicolor
#619CFF   virginica
Community
  • 1
  • 1
air
  • 123
  • 1
  • 6

2 Answers2

5

Welcome to 2021!

In ggplot version 3.3.3, we can do this:

g <- ggplot_build(p)
data.frame(colours = unique(g$data[[1]]["colour"]),
             label = gb$plot$scales$scales[[4]]$get_labels()) 

The get_labels() function hidden in the object gets you the corresponding labels for your colors. I believe the magic '[[4]]' on the third line is because my plot had x, y, shape and color scales too.

Jeff
  • 695
  • 1
  • 8
  • 19
4

Something like this maybe:

#get the colours as mentioned in your question
#and you could get the levels from the plot's data
data.frame(colours = unique(g$data[[1]]["colour"]), 
             label = levels(g$plot$data[, g$plot$labels$colour]))

Output:

     colour      label
1   #F8766D     setosa
51  #00BA38 versicolor
101 #619CFF  virginica

Update:

If there is a p <- p + scale_color_discrete(labels=c("sp1","sp2","sp3")) then you could do:

g <- ggplot_build(p)
data.frame(colours = unique(g$data[[1]]["colour"]), 
             label = g$plot$scales$scales[[1]]$labels)

Which outputs:

     colour label
1   #F8766D   sp1
51  #00BA38   sp2
101 #619CFF   sp3
LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • Thanks. The problem with this answer is that I cannot hardcode Species (i.e. I would also need to figure this out from g). Also what if the user manually specified labels via scale_color_discrete(labels=...)? – air Nov 25 '15 at 22:11
  • Look into `str(p)`. There "Species" is stored as `p$labels$colour`. Then `levels(p[["data"]][[p$labels$colour]])` does the trick. – mts Nov 25 '15 at 22:14
  • 2
    I am using `g` to create the `data.frame` and as @mts mentions in his comment you could use `g$plot$labels$colour` so that you do not hardcode Species . I have updated the answer. – LyzandeR Nov 25 '15 at 22:17
  • Thanks to both of you @LyzandeR and @mts. But it feels a bit like a hack, e.g. again consider `p <- p + scale_color_discrete(labels=c("sp1","sp2","sp3"))`. Then I would like to extract the label shown in the plot, i.e. `c("sp1","sp2","sp3")`, while your method still returns the original levels. – air Nov 25 '15 at 22:22
  • Also another case when it fails is when the factor includes some levels which do not appear in the data (e.g. because the data was subset/filtered from a bigger dataset). I guess this would be more straightforward to patch, but again it would be hacky. – air Nov 25 '15 at 22:31
  • 1
    Well when you use the `ggplot` objects in different ways, then data is stored in the object in different places. I don't think there is a generic way to extract the legend entries irrespective of the way they were created. You could create a function with `if-cases` that could potentially pick the correct place according to the existance of specific elements in the ggplot object. – LyzandeR Nov 25 '15 at 22:33
  • @LyzandeR Ok thanks again. I will accept tomorrow if no better answer comes along. What I am really trying to do is to create a function which takes a ggplot2 object and automatically makes pretty legends as described in [the following blog post](http://www.r-bloggers.com/coloring-and-drawing-outside-the-lines-in-ggplot/). This would require a quite generic solution though. – air Nov 25 '15 at 22:40
  • 1
    np happy to help @air :). I am sure you would be able to code up a function that finds the legend keys every time. I don't think there are many if-cases. I would also like to see if there is a generic solution that I don't know of (maybe in a different package?), but I don't think I am. – LyzandeR Nov 25 '15 at 22:46