Order stacked ggplot2 percentage bar plot by y con

2019-08-04 12:37发布

问题:

(reposted - previously flagged as duplicate but additional MRE illustrates issue more completely)

I'm trying to order one of the factors in a stacked percentage bar plot by the percentage value of that factor but can't manage to work out how to do it.

e.g. in the following I'd like the categories in x to be ordered by the percentage value of factor q.

library(tibble)
library(ggplot2)

df <- tibble(x = rep(LETTERS[1:5], 2), y = c(10,20,5,60,30,90,80,95,40,70), z = c(rep("p", 5), rep("q", 5)))

ggplot(df, aes(x = x, y = y, fill = z)) +
   geom_bar(stat = "identity", position = "fill") +
   scale_y_continuous(labels=scales::percent)

The solution accepted at question Reorder bars in geom_bar ggplot2 uses reorder() to change the order of x bars, but does not work for my case:

ggplot(df, aes(x = reorder(x, -y), y = y, fill = z)) +
  geom_bar(stat = "identity", position = "fill") +
  scale_y_continuous(labels=scales::percent)

Produces exactly the same plot as above.

I think the reason is that the data are in tidy format so there are multiple y values for x, but I can't figure out how to map only a subset of y onto x.

回答1:

How about first defining the levels:

df %>%
  filter(z == "q") %>%
  mutate(x = factor(x, levels = x[order(y)])) %>%
  pull(x) %>%
  levels -> x_levels

and then using those levels as input to ggplot:

df %>%
  mutate(x = factor(x,
                    levels = x_levels)) %>%
  ggplot(aes(x = x,
             y = y,
             fill = z)) +
  geom_bar(stat = "identity",
           position = "fill") +
  scale_y_continuous(labels = scales::percent)

another way is to use two accessory functions:

available on github dgrtwo/drlib

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


ggplot(df, aes(x = reorder_within(x, y, 1, fun = max),
               y = y,
               fill = z)) + 
  geom_bar(stat = "identity",
           position = "fill") +
  scale_y_continuous(labels = scales::percent)+
  scale_x_reordered()

I used 1 as within group, but these functions allow for a more complicated sort within each facet.

or:

  ggplot(df, aes(x = reorder_within(x, y, 1, fun = min),
                   y = y,
                   fill = z)) + 
      geom_bar(stat = "identity",
               position = "fill") +
      scale_y_continuous(labels = scales::percent)+
      scale_x_reordered()



标签: r ggplot2