8

If I want to order the bars in a ggplot2 barchart from largest to smallest, then I'd usually update the factor levels of the bar category, like so

one_group <- data.frame(
  height   = runif(5),
  category = gl(5, 1)
)

o <- order(one_group$height, decreasing = TRUE)
one_group$category <- factor(one_group$category, levels = one_group$category[o])

p_one_group <- ggplot(one_group, aes(category, height)) +
  geom_bar(stat = "identity")
p_one_group

If have have several groups of barcharts that I'd like in different facets, with each facet having bars ordered from largest to smallest (and different x-axes) then the technique breaks down.

Given some sample data

two_groups <- data.frame(
  height   = runif(10),
  category = gl(5, 2),
  group    = gl(2, 1, 10, labels = letters[1:2])
)

and the plotting code

p_two_groups <- ggplot(two_groups, aes(category, height)) +
  geom_bar(stat = "identity") +
  facet_grid(. ~ group, scales = "free_x")
p_two_groups

what do I need to do to get the bar ordering right?

If it helps, an equivalent problem to solve is: how do I update factor levels after I've done the faceting?

Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
  • 1
    Nice question. I can't immediately think of an answer, only alternatives. 1) Use align.plots in package ggExtra. 2) Construct separate, individual graphs and combine them using grid viewports - vplayout in ggExtra simplifies this. If this seems useful, I can post an example in an answer. – Andrie Mar 23 '11 at 18:34

2 Answers2

15

here is a hack:

two_groups <- transform(two_groups, category2 = factor(paste(group, category)))
two_groups <- transform(two_groups, category2 = reorder(category2, rank(height)))

ggplot(two_groups, aes(category2, height)) +
  geom_bar(stat = "identity") +
  facet_grid(. ~ group, scales = "free_x") +
  scale_x_discrete(labels=two_groups$category, breaks=two_groups$category2)
  1. make UNIQUE factor variable for all entries (category2)
  2. reorder the variable based on the height
  3. plot on the variable: aes(x=category2)
  4. re-label the axis using original value (category) for the variable (category2) in scale_x_discrete.
kohske
  • 65,572
  • 8
  • 165
  • 155
3

Here is a hack to achieve what you want. I was unable to figure out how to get the category values below the tick marks. So if someone can help fix that, it would be wonderful. Let me know if this works

# add a height rank variable to the data frame
two_groups = ddply(two_groups, .(group), transform, hrank = rank(height));

# plot the graph

p_two_groups <- ggplot(two_groups, aes(-hrank, height)) +
  geom_bar(stat = "identity") +
  facet_grid(. ~ group, scales = "free_x") +
  opts(axis.text.x = theme_blank()) +
  geom_text(aes(y = 0, label = category, vjust = 1.5))
Ramnath
  • 54,439
  • 16
  • 125
  • 152
  • Nice `ddply`/`transform` combo. I don't think that `geom_text` works outside the plotting panels; overriding the scale as per kohske's answer is a cleaner fix. – Richie Cotton Mar 24 '11 at 10:41
  • yup. i was unable to figure out how to overcome that limitation. great hack from kohske. – Ramnath Mar 24 '11 at 14:37