sorting horizontal bars per facet in ggplot?

Hello there,

Consider this simple example


library(tibble)
library(ggplot2)
library(dplyr)

tibble(group = c(1,1,1,1,2,2,2,2),
       label = c('a','b','c','d','e','f','a','b'),
       value = c(10,9,8,7,20,2,12,1)) %>% 
  ggplot(aes(x = as.factor(label), y = value, fill = as.factor(group))) + 
  geom_bar(stat = 'identity') + coord_flip() +
  facet_wrap(~ as.factor(group))

My problem is that I would like EACH facet to be sorted from the label with the highest value to the label to the lowest value. For instance in group 1 the highest label is a, while in group 2 its e.

How can I do that?
Thanks!

You can do it this way

library(tidyverse)

reorder_within <- function(x, by, within, fun = mean, sep = "___", ...) {
    new_x <- paste(x, within, sep = sep)
    stats::reorder(new_x, by, FUN = fun)
}

scale_x_reordered <- function(..., sep = "___") {
    reg <- paste0(sep, ".+$")
    ggplot2::scale_x_discrete(labels = function(x) gsub(reg, "", x), ...)
}

tibble(group = c(1,1,1,1,2,2,2,2),
       label = c('a','b','c','d','e','f','a','b'),
       value = c(10,9,8,7,20,2,12,1)) %>% 
    ggplot(aes(x = reorder_within(label, value, group), y = value, fill = as.factor(group))) + 
    geom_bar(stat = 'identity') + 
    coord_flip() +
    scale_x_reordered() +
    facet_wrap(~ as.factor(group), scales = "free_y")

The author of the helper functions is David Robinson, here is the link to the github repo

3 Likes

wow thanks a lot! but do you mind explaining what you do here? would that work even with, say 4 groups in a 2x2 facet_wrap ordering?

Yes, this basically creates new factors for each grouping combination, arranges by those factors and then replaces the labels by the original values.

library(tidyverse)

reorder_within <- function(x, by, within, fun = mean, sep = "___", ...) {
    new_x <- paste(x, within, sep = sep)
    stats::reorder(new_x, by, FUN = fun)
}


scale_x_reordered <- function(..., sep = "___") {
    reg <- paste0(sep, ".+$")
    ggplot2::scale_x_discrete(labels = function(x) gsub(reg, "", x), ...)
}

tibble(group = c(1,1,2,2,3,3,4,4),
       label = c('a','b','c','d','e','f','a','b'),
       value = c(10,9,8,7,20,2,12,1)) %>% 
    ggplot(aes(x = reorder_within(label, value, group), y = value, fill = as.factor(group))) + 
    geom_bar(stat = 'identity') + 
    coord_flip() +
    scale_x_reordered() +
    facet_wrap(~ as.factor(group), scales = "free_y")

4 Likes

Just a quick note that reorder_within() is now part of the the tidytext package (as of version 0.2.1), so if you have that package, no need to define it yourself.

https://juliasilge.com/blog/reorder-within/

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.