2

I am trying to change the order in which my legend displays the points and lines using ggplot for my graph.

Currently I have my graph itself displaying dots first then lines on top of the dots which is what I want. Here is a reproducible example:

ggplot(iris, aes(Sepal.Width, Petal.Width, linetype = Species)) +
  geom_point(aes(colour = Species, shape = Species)) +
  geom_smooth()

enter image description here

As you can see from the legend, it has overlayed the values in the order plotted, but I would like to reverse this. I know that I could overlay the points on top of the plot as follows:

ggplot(iris, aes(Sepal.Width, Petal.Width, linetype = Species)) +
  geom_smooth() +
  geom_point(aes(colour = Species, shape = Species)) 

enter image description here

I have searched and found the function guides(fill = guide_legend(reverse = TRUE)) from here, but this is having no effect.

Any help much appreciated

Michael Harper
  • 14,721
  • 2
  • 60
  • 84
  • Hi Justin, Please edit your question and provide a Minimal, Complete, and Verifiable example, in particular a sample data set to reproduce the problem: https://stackoverflow.com/help/mcve . Makes helping you much easier :) – Michael Harper Dec 05 '17 at 23:09
  • I ended up just rewriting the question so you had a better idea of how to structure it for the future. Please check the edit and approve it :) – Michael Harper Dec 05 '17 at 23:35
  • Hi Mikey, Thank you very much for taking the time to teach me how to properly structure the question and provide a verifiable example I will certainly learn from your example and do this on my own in the future. I approve the edits absolutely :) – Justin Hubbard Dec 06 '17 at 02:16

2 Answers2

3

From what I have read in the ggplot descriptions, I don't believe this is possible to set the order of overlaying ggplot legends. Within the guides function, you can set the order of the separate guides using the order argument, which it explains:

"positive integer less that 99 that specifies the order of this guide among multiple guides. This controls the order in which multiple guides are displayed, not the contents of the guide itself. If 0 (default), the order is determined by a secret algorithm."

It seems easiest to me to achieve what you want through a combination of:

  1. Using legend=FALSE to suppress certain legends
  2. Plotting some layers multiple times.

In the example below, the geom_smooth is plotted twice. Once to create the legend background, and then second to overlay the data. The second time it is plotted, the legend isn't shown:

ggplot(iris, aes(Sepal.Width, Petal.Width, linetype = Species)) +
  geom_smooth() +
  geom_point(aes(colour = Species, shape = Species)) +
  geom_smooth(show.legend = FALSE)

enter image description here

Another approach would be to make the line colour change with each dataset. This requires less playing around and is clearer to understand, so I would probably go with this:

ggplot(iris, aes(Sepal.Width, Petal.Width, colour = Species, shape = Species, linetype = Species)) +
  geom_point(aes())  +
  geom_smooth(aes())

enter image description here

Michael Harper
  • 14,721
  • 2
  • 60
  • 84
2

Here are two methods that will allow the order in which grobs in the legend are drawn to be manipulated, but neither is straightforward.

The first uses ggplot's gtable. The column "z" in the layout data frame gives the order in which the grobs are drawn. But one has to get to the layout data frame of the legend itself in order to manipulate the legend's grobs. Something like this:

library(ggplot2)
library(gtable)
library(grid)

df = read.table(text = 
"School      Year    Value 
 A           1998    5
 B           1999    10
 C           2000    15
 A           2000    7
 B           2001    15
 C           2002    20", sep = "", header = TRUE)

p <- ggplot(iris, aes(Sepal.Width, Petal.Width, linetype = Species)) +
  geom_point(aes(colour = Species, shape = Species), size = 2) +
  geom_smooth()

# Get the ggplot grob
gt <- ggplotGrob(p)

# Get the legend
leg <- gt$grobs[gt$layout$name == "guide-box"][[1]]$grobs[[1]]

# Check the layout
leg$layout

lines 4 and 5 draw the first legend key, but it draws the point first (key-3-1-1) then the line segment (key-3-1-2). Swapping the z-values will ensure that the line segment is plotted first, then the point. Similarly, for lines 7 & 8, and lines 10 & 11.

# Swap the z-values for the relevant lines in the layout table
leg$layout[c(4:5, 7:8, 10:11), "z"] <- c(5:4, 8:7, 11:10)

# Put legend back into ggplot grob
gt$grobs[gt$layout$name == "guide-box"][[1]]$grobs[[1]] <- leg

# Draw it
grid.newpage()
grid.draw(gt)

The second method uses grid's editing functions to change the order.

# Get the ggplot grob
gt <- ggplotGrob(p)

# Get the legend grob
leg = getGrob(grid.force(gt), gPath("guides"), grep = TRUE)

# Get a list of grobs (in the legend).
# This also gives the order.
grid.ls(grid.force(leg))

# Nearly the same names for the grobs, 
#   and the same order as before 
#   (don't count the indented grobs).

# Change the order in which grobs are drawn
leg = reorderGrob(leg,  c(5:4, 8:7, 11:10), back = FALSE)

# Remove the original legend and replace with the new legend
gt = removeGrob(grid.force(gt), "guides", grep = TRUE)
gt = addGrob(gt, leg, "guide-box", grep = TRUE)

# Draw the plot
grid.newpage()
grid.draw(gt)

enter image description here

Sandy Muspratt
  • 31,719
  • 12
  • 116
  • 122