How to rank categories in grouped barplot?

Viewed 131

Here is data of sales by year and model. Now I wan to rank/order these models by following:

  1. Models has to be ordered from top to down according to their sales in each year. For example, E had the biggest sales in 2015, so it must the on the TOP, while in 2017 H must be on the TOP.

  2. Moreover, I need to keep model J always on the bottom regardless its share.

library(dplyr)

library(ggplot2)

        
df <- data.frame (model  = c("A","B","C","D","E","F","G","H","I","J","A","B","C","D","E","F","G","H","I","J","A","B","C","D","E","F","G","H","I","J","A","B","C","D","E","F","G","H","I","J"),
                          
    Year = c(2015,2015,2015,2015,2015,2015,2015,2015,2015,2015,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2018,2018,2018,2018,2018,2018,2018,2018,2018,2018),
    sales = c(450,678,456,344,984,456,234,244,655,789,234,567,234,567,232,900,1005,1900,450,345,567,235,456,345,144,333,555,777,111,444,222,223,445,776,331,788,980,1003,456,434))
    
    
     df %>% 
      group_by(Year) %>%
      mutate(Share = sales / sum(sales)) %>%
      mutate_at(vars(Share), funs(round(., 4))) %>%
      ggplot(aes(fill=model, y=Share, x=Year))+
      scale_x_continuous(breaks=seq(min(df$Year),max(df$Year),2))+
      geom_col(position="fill", width = 1, color = "white") +
      geom_text(aes(label = scales::percent(Share, accuracy = 0.1)), 
                position = position_fill(vjust = 0.50),
                color = "black",size = 2) +
      scale_y_continuous(labels = scales::percent) 

enter image description here

1 Answers

You need to add the stacked columns per year to be able to use individual sorting on each column. Making use of some tidyverse helpers you can try the following:

library(purrr)
library(forcats)

## split the data according to year and order factors according to need
## df_agg will eb a list(!) of data frames where each model is sorted accordingly
## Adding `Year2` is a quick hack as `group_map` drops the grouping variable

df_agg <- df %>% 
  group_by(Year) %>%
  mutate(Share = sales / sum(sales), Year2 = Year) %>%
  mutate(across(Share, ~ round(., 4))) %>% 
  group_by(Year) %>% 
  group_map(~ .x %>% 
              mutate(model = fct_reorder(model, Share, .desc = TRUE) %>% 
                             fct_relevel("J", after = Inf)) %>% 
              rename(Year = Year2))

## base plot

bp <- ggplot() +
  scale_x_continuous(breaks = seq(min(df$Year), max(df$Year), 2))+
  scale_y_continuous(labels = scales::percent)

## use purrr::reduce to add geoms for each year to the baseplot

reduce(df_agg, ~ .x + 
         geom_col(aes(x = Year, y = Share, fill = model), data = .y, 
                               position = "fill", width = 1, color = "white") +
         geom_text(aes(x = Year, y = Share, fill = model,
                       label = scales::percent(Share, accuracy = 0.1)),
                   data = .y,
                   position = position_fill(vjust = 0.50),
                   color = "black", size = 2), .init = bp)

Stacked Barplot with catgeories sorted individually per column

N.B. Personally, I find the plot hard to read as you always need to refer to the legend to determine which model is shown.

Related