21
votes

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?

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
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
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

1 Answers

18
votes

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.