SlowLearner
SlowLearner

Reputation: 8017

R: ordering facets by value rather than alphabetical order in a ggplot2 plot

A couple of weeks ago I used ggplot2 to create a faceted plot where the facets were ordered by the last value in the data frame. I had no major problems until it came to reordering, as I haven't really assimilated all the complications of orders, factors and levels. Still, after an hour or two (or three) of referring to SO posts I got it working.

When I came back to the script today it was no longer "working" in that it is now sorting the facets by alphabetical order rather than by the final value of the data frame. (I think that I originally "fixed" the problem while messing around at the R console and did not actually add the solution to the script.) Rather than spending another couple of hours on this tonight I'm going to throw myself on the mercy of SO.

Q. How can I sort the facets by a specified value rather than by the alphabetical order of the names of each facet? Please note the following code is an example only; the real data has several dozen items.

Edited code below to reflect additional input from @joran; the facets are now sorted and filled appropriately. Mission successful.

# Version 3
require(ggplot2) ## NB This script assumes you have ggplot2 v0.90
require(scales)
require(plyr)
require(lubridate)
require(reshape)

set.seed(12345)
monthsback <- 15
date <- as.Date(paste(year(now()),month(now()),"1",sep="-")) - months(monthsback)
myitems <- data.frame(mydate=seq(as.Date(date), by="month", length.out=monthsback),
                      aaa = runif(monthsback, min = 600, max = 800),
                      bbb = runif(monthsback, min = 100, max = 200),
                      ccc = runif(monthsback, min = 1400, max = 2000),
                      ddd = runif(monthsback, min = 50, max = 120))

myitems <- melt(myitems, id = c('mydate'))

change_from_start <- function(x) {
   (x - x[1]) / x[1]
}

myitems <- ddply(myitems, .(variable), transform, value = change_from_start(value))
myitems$mydate <- as.Date(myitems$mydate, format = "%Y-%m-%d")
myvals <- myitems[myitems$mydate == myitems$mydate[nrow(myitems)],] # get values on which to sort facets
myvals <- within(myvals, variable <- factor(variable, as.character(myvals[order(myvals$value, decreasing = T),]$variable),ordered = TRUE))
myitems <- within(myitems, variable <- factor(variable, as.character(myvals[order(myvals$value, decreasing = T),]$variable),ordered = TRUE))
print(levels(myitems$variable)) # check to see if ordering succeeded
myitems$fill <- ifelse(myitems$variable == "ddd", "blue", "darkgreen")

    p <- ggplot(myitems, aes(y = value, x = mydate, group = variable)) +
      geom_rect(aes(xmin = as.Date(myitems$mydate[1]), xmax = Inf, fill = fill), ymin = -Inf, ymax = Inf) +
      scale_fill_manual(values = c("blue", "darkgreen")) +
      geom_line(colour = "black") +
      geom_text(data = myvals, aes(x = as.Date(myitems$mydate[1]) + 250, y = 0.2, label = sprintf("%1.1f%%", value * 100))) +
      facet_wrap( ~ variable, ncol = 2) +
      geom_hline(yintercept = 0, size = 0.6, linetype = "dotdash") +
      scale_y_continuous(label = percent_format()) +
      scale_x_date(expand = c(0,0), labels = date_format("%Y-%m"), breaks = date_breaks("year")) +
      xlab(NULL) +
      ylab(NULL) +
      opts(legend.position = "none") +
      opts(panel.grid.minor = theme_blank()) +
      opts()

print(p)

Image showing that facets are now sorted properly but that the fill is no longer working

Upvotes: 8

Views: 6280

Answers (2)

joran
joran

Reputation: 173737

You have two problems:

  1. The line that converts myitems$variable to a factor should specify ordered = TRUE, to assure that it will be an ordered factor.

  2. Your geom_text call uses a separate data frame whose corresponding variable isn't a factor (or ordered) so it's stomping on the ordered nature of the one in myitems.

Convert them both or ordered factors, and you should be fine.

Upvotes: 4

b_rousseau
b_rousseau

Reputation: 169

Facet are ordered in the same order as variables in source data.frame.
Thus as a basic hack you can just order variables name when creating the data.frame :

myitems <- data.frame(mydate=seq(as.Date(date), by="month", length.out=monthsback),
        'ccc' = runif(monthsback, min = 1400, max = 2000),
        'aaa' = runif(monthsback, min = 600, max = 800),
        'ddd' = runif(monthsback, min = 50, max = 120),
        'bbb' = runif(monthsback, min = 100, max = 200)
        )

If you need to reorder at the end of the process, then arrange() might be the best solution.

Upvotes: 0

Related Questions