21

I stumbled onto this weird behavior with ggplot2s ordering of legends and just can't figure out what the logic behind the automatic placement of the legends is:

My aim: In a plot with multiple scales I want to arrange them in a different (thematic) order than the automatic one. But I couldn't find a command in opts() or guides() to do this for me. And just to be clear: I don't want to change the items within the legends, that works fine, but the arrangement of multiple complete legends.

So first I assumed they were ordered by type, i.e. scale, colour etc. But that is not the case, their position changes (see below).

Alphabetical order? No.

    library(ggplot2) ## v0.9

    ## Scale_colour on top
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class) +
    scale_size(name = "A") + scale_colour_discrete(name = "B")

    ## Reverse names --> scale_colour on bottom
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class) +
    scale_size(name = "B") + scale_colour_discrete(name = "A")

    ## Change name B to C -->  scale_colour on bottom
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class) +
    scale_size(name = "C") + scale_colour_discrete(name = "A")

    ## Change name B to D -->  scale_colour on top
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class) +
    scale_size(name = "D") + scale_colour_discrete(name = "A")

Further positions of scale_colour (for exchanged scale_size name)

  • "E": bottom
  • "F" - "L": top
  • "M" - "N": bottom

and it continues to appear on top an at the bottom.

Factorial order? No.

    ## From top to bottom: C - B - A
    fname <- factor(c("A","B","C"), levels = c("A","B","C"))
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class, alpha = cyl) +
    scale_size(name = fname[1]) + scale_colour_discrete(name = fname[2]) + scale_alpha(name=fname[3])

    ## From top to bottom: B - C - A
    fname <- factor(c("A","B","C"), levels = c("C","B","A"))
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class, alpha = cyl) +
    scale_size(name = fname[1]) + scale_colour_discrete(name = fname[2]) + scale_alpha(name=fname[3])

    ## From top to bottom: B - C - A
    fname <- factor(c("A","B","C"), levels = c("B","C","A"))
    qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class, alpha = cyl)+
    scale_size(name = fname[1]) + scale_colour_discrete(name = fname[2]) + scale_alpha(name=fname[3])

Length of title? No. I'll stop for now with example code, but that one also yielded fluctuating orders independent of character length.

Any ideas?

bleutner
  • 535
  • 4
  • 8
  • 1
    Interestingly, for your 1st set of examples, prepending a space to each name *does* cause the legends to appear in alphabetical order. (i.e.: `scale_size(name = " B") + scale_colour_discrete(name = " C")`). *That* method, though, again fails when there are more than two legends to be arranged... – Josh O'Brien Apr 05 '12 at 21:18
  • 1
    I will be very interested in the answer to this question, although I suspect it will require someone like Kohske or Hadley to weigh in. I could not find any discussion of this from the mailing list, but it seems like something that should have come up at some point. – joran Apr 05 '12 at 21:28
  • Unfortunately, now the order of the legend boxes depends on the `hash` of them, so there is no way to predict the order... Definitely this is bad idea of my implementation. I (or Hadley or WCH) will fix this problem in the future version. – kohske Apr 06 '12 at 00:12
  • possible duplicate of [Controlling ggplot2 legend display order](http://stackoverflow.com/questions/11393123/controlling-ggplot2-legend-display-order) – hadley Mar 28 '13 at 14:13

1 Answers1

21

As I mentioned in the comment above, there is no way to control and predict the position of legend box. I wasn't aware of this problem. Thank you for making clear this.

Maybe some people need to control the legend box, here I put a quick fix:

# run this code before calling ggplot2 function
guides_merge <- function(gdefs) {
  gdefs <- lapply(gdefs, function(g) { g$hash <- paste(g$order, g$hash, sep = "z"); g})
  tapply(gdefs, sapply(gdefs, function(g)g$hash), function(gs)Reduce(guide_merge, gs))
}
environment(guides_merge) <- environment(ggplot)
assignInNamespace("guides_merge", guides_merge, pos = "package:ggplot2")

and then you can use order argument for guide_legend (and also guide_colorbar),

# specify the order of the legend.
qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class, alpha = cyl)+
 guides(size = guide_legend(order = 1), colour = guide_legend(order = 2), alpha = guide_legend(order = 3))

qplot(data = mpg,x = displ, y = cty, size = hwy, colour = class, alpha = cyl)+
 guides(size = guide_legend(order = 3), colour = guide_legend(order = 1), alpha = guide_legend(order = 2))

order argument should be a positive integer. The legends are arranged along the order. Note that this is a quick fix so the interface may be changed in the next official version of ggplot2.

kohske
  • 65,572
  • 8
  • 165
  • 155
  • 7
    Kohske provides an updated version of this answer (which uses changes implemented in ggplot2 since this answer was given) at http://stackoverflow.com/a/11397958/892313 – Brian Diggs Jul 09 '12 at 16:38