1

How can I add a second legend for the shape aesthetics in ggplot?

The chart shows different start types (shape), different processes (color), and different end types (shape again). Currently, I am only able to display two legends: one for the color and one for the shape (which merges start.type and end.tpye). Any help on how to get the separate third legend (for the end.type)? Introducing linetype as an additional aesthetics is not an option. Many thanks.

id <- c("1","2","3")
start.date <- c("01/01/2010","05/05/2004","01/08/2006")
end.date <- c("31/12/2012","05/05/2007","01/09/2009")
start.type <- c("x1","x2","x3")
end.type <- c("y1","y2","y3")
process.type <- c("p1","p2","p3")
u <- data.frame(id,start.date, end.date,start.type,end.type,process.type)

u.plot <- ggplot(u)+
  geom_segment(aes(color=process.type, x=start.date, xend=end.date, y=id, yend=id), size=1)+
  geom_point(aes(shape=start.type, x=start.date, y=id), size=3)+
  geom_point(aes(shape=end.type, x=end.date, y=id), size=3)

plot(u.plot)

enter image description here

zoowalk
  • 2,018
  • 20
  • 33
  • 1
    Standard ggplot only allows one legend per aesthetic. If you want to, you're going to have to hack it heavily. Is that something you want to do? It would probably be easier to edit it after the fact in a graphics program to split part the legend. It depends on how important this is to you. – MrFlick Jul 21 '14 at 19:39
  • i was already afraid that this would be the case. quite surprised that there isn't a standard way for this. Problem is this (a similar) code runs in a loop over numerous dataframes. but thanks anyway. – zoowalk Jul 21 '14 at 22:36

1 Answers1

4

enter image description here

editing the legend is likely to be painful and easily broken, but if you must try here's a quick and dirty way

require(gtable)
g = ggplotGrob(u.plot) # calls ggplot_build and ggplot_gtable
a = g$grobs[[8]][["grobs"]][[2]] # extract the legend (a gtable itself)
a = gtable_add_rows(a, unit(c(0.5,1),"line"), 6) # add two rows
new = editGrob(a[["grobs"]][[2]], label = "I'm in") # copy legend title with new text

a = gtable_add_grobs(a, new, 8, l=2, r=5) # place the new title
g$grobs[[8]][["grobs"]][[2]] = a # replace with new legend in the plot
grid.newpage()
grid.draw(g)
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • many thanks. this looks already very promising. if you have a second, would you mind briefly commenting the separate steps. I am not sure I understand them entirely. ggplotGrob help seems very limited (unless I missed sth). in principle, should this approach also work - with modifications - when working with facets? – zoowalk Jul 22 '14 at 11:38
  • 1
    the gtable package provides some documentation for the relevant functions; there's also an introductory [wiki page](https://github.com/baptiste/gtable/wiki/Description) – baptiste Jul 22 '14 at 14:13
  • I think a more reliable strategy would be to produce two versions of the plot, each with a specific (trimmed down) shape legend, extract the gtable corresponding to one of the legend and merge it with the other (rbind). – baptiste Jul 22 '14 at 14:16
  • a = gtable_add_grobs(a, new, 8, l=2, r=5) # place the new title; could it be that it should be gtable_add_grob (without s) at the end? – zoowalk Jul 22 '14 at 17:19
  • probably, I always thought it should be plural, so I made it so on my fork. – baptiste Jul 22 '14 at 17:24