Reputation: 8017
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)
Upvotes: 8
Views: 6280
Reputation: 173737
You have two problems:
The line that converts myitems$variable
to a factor should specify ordered = TRUE
, to assure that it will be an ordered factor.
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
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