7

I am trying to create several individual plots from the same data.frame with a different order of the factor levels on the y axis for each plot. Each plot is supposed to order the factor levels on y decreasingly.

I know that this can be done manually for each plot but I am looking for a more efficient and elegant way since I'll have quite a number of plots that I need to create. This doesn't have to include the use of of facet_wrap, if there is another way, maybe with loops etc.?

library(ggplot2)
library(dplyr)
data("diamonds")

Taking the data set and aggregating by two factor levels (clarity and cut):

means <- diamonds %>%
group_by(clarity, cut) %>%
summarise(carat = mean(carat))

Here I reorder by mean of one factor but eventually I would like to reorder separately for each plot (by decreasing mean of clarity).

means$clarity <- reorder(means$clarity, means$carat, FUN = mean)

Creating separate plots with face_wrap. Using coord_flip to compare plots more easily.

ggplot(means, aes(x = clarity, y = carat)) +
  geom_col() +
  facet_wrap(~cut, ncol = 1) +
  coord_flip()

You'll see that this creates separate plots for each type of cut but the order of the factor levels on the y axis are not correct for each individual case. How can I order them correctly without having to do that manually for each type of cut?

mx0
  • 6,445
  • 12
  • 49
  • 54
SveaM
  • 71
  • 5
  • 2
    Obviously not exactly what you want, but since you asked for an elegant solution, I can only think of writing a function that creates individual plots (using `ggplot(means, aes(x = reorder(clarity, carat), y = carat))`, `lapply` through the facets and then reunite the single plots using `gridExtra`. It would add only a couple of lines to your code. – yoland Nov 30 '17 at 12:21

1 Answers1

7

This can be done in one plot by utilizing two functions:

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), ...)
}

available on github dgrtwo/drlib

ggplot(means, aes(x = reorder_within(clarity, carat, cut, mean), y = carat)) + 
  geom_col() + 
  scale_x_reordered() +
  facet_wrap(~cut,  scales = "free_y", ncol = 1) +
  coord_flip()

enter image description here

missuse
  • 19,056
  • 3
  • 25
  • 47