Controlling the order of points in ggplot2?

I'm plotting a dense scatter plot in ggplot2 where each point might be labeled by a different color:

df <- data.frame(x=rnorm(500))
df$y = rnorm(500)*0.1 + df$x
df$label <- c("a")
df$label[50] <- "point"
df$size <- 2

ggplot(df) + geom_point(aes(x=x, y=y, color=label, size=size))

When I do this, the scatter point labeled "point" (green) is plotted on top of the red points which have the label "a". What controls this z ordering in ggplot, i.e. what controls which point is on top of which?

For example, what if I wanted all the "a" points to be on top of all the points labeled "point" (meaning they would sometimes partially or fully hide that point)? Does this depend on alphanumerical ordering of labels?

I'd like to find a solution that can be translated easily to rpy2.


ggplot2 will create plots layer-by-layer and within each layer, the plotting order is defined by the geom type. The default is to plot in the order that they appear in the data.

Where this is different, it is noted. For example

geom_line

Connect observations, ordered by x value.

and

geom_path

Connect observations in data order


There are also known issues regarding the ordering of factors, and it is interesting to note the response of the package author Hadley

The display of a plot should be invariant to the order of the data frame - anything else is a bug.


This quote in mind, a layer is drawn in the specified order, so overplotting can be an issue, especially when creating dense scatter plots. So if you want a consistent plot (and not one that relies on the order in the data frame) you need to think a bit more.


Create a second layer

If you want certain values to appear above other values, you can use the subset argument to create a second layer to definitely be drawn afterwards. You will need to explicitly load the plyr package so .() will work.

set.seed(1234)
df <- data.frame(x=rnorm(500))
df$y = rnorm(500)*0.1 + df$x
df$label <- c("a")
df$label[50] <- "point"
df$size <- 2
library(plyr)
ggplot(df) + geom_point(aes(x = x, y = y, color = label, size = size)) +
  geom_point(aes(x = x, y = y, color = label, size = size), 
             subset = .(label == 'point'))

enter image description here

Update

In ggplot2_2.0.0, the subset argument is deprecated. Use e.g. base::subset to select relevant data specified in the data argument. And no need to load plyr:

ggplot(df) +
  geom_point(aes(x = x, y = y, color = label,  size = size)) +
  geom_point(data = subset(df, label == 'point'),
             aes(x = x, y = y, color = label, size = size))

Or use alpha

Another approach to avoid the problem of overplotting would be to set the alpha (transparancy) of the points. This will not be as effective as the explicit second layer approach above, however, with judicious use of scale_alpha_manual you should be able to get something to work.

eg

# set alpha = 1 (no transparency) for your point(s) of interest
# and a low value otherwise
ggplot(df) + geom_point(aes(x=x, y=y, color=label, size=size,alpha = label)) + 
  scale_alpha_manual(guide='none', values = list(a = 0.2, point = 1))

enter image description here


2016 Update:

The order aesthetic has been deprecated, so at this point the easiest approach is to sort the data.frame so that the green point is at the bottom, and is plotted last. If you don't want to alter the original data.frame, you can sort it during the ggplot call - here's an example that uses %>% and arrange from the dplyr package to do the on-the-fly sorting:

library(dplyr)
ggplot(df %>%
         arrange(label),
       aes(x = x, y = y, color = label, size = size)) +
  geom_point()

enter image description here

Original 2015 answer for ggplot2 versions < 2.0.0

In ggplot2, you can use the order aesthetic to specify the order in which points are plotted. The last ones plotted will appear on top. To apply this, you can create a variable holding the order in which you'd like points to be drawn.

To put the green dot on top by plotting it after the others:

df$order <- ifelse(df$label=="a", 1, 2)
ggplot(df) + geom_point(aes(x=x, y=y, color=label, size=size, order=order))

Or to plot the green dot first and bury it, plot the points in the opposite order:

ggplot(df) + geom_point(aes(x=x, y=y, color=label, size=size, order=-order))

For this simple example, you can skip creating a new sorting variable and just coerce the label variable to a factor and then a numeric:

ggplot(df) +
  geom_point(aes(x=x, y=y, color=label, size=size, order=as.numeric(factor(df$label))))