Search code examples
rggplot2colorsstacked-chart

Changing colors of a stacked bar plot but keeping the stacks of each class in increasing order


I have been trying to create a stacked bar plot, where the colors of the plot relate to those in a map. Basically, I have proportion of an area covered by a given polygon. I have sorted my df in advance so the stacks Proportion are in decreasing order per each Class. It works ok if a set the values of the fill as a continuos variable i.e. Cluster (but then I can't change particular colors of the stack), and if I convert them into factors Clu then the order of the stacks is lost or I can manage to sort them but for the whole graph not for each class... The same Cluster can occur in different classes i.e. cluster Two

          Num    Class Cluster Proportion   Clu Order consec
1    9  Class_9       2      0.859   Two     1      1
2    9  Class_9       5      0.141  Five     2      2
3   10 Class_10       2      0.622   Two     1      3
4   10 Class_10       1      0.179   One     2      4
5   10 Class_10       7      0.165 Seven     3      5
6   10 Class_10       6      0.034   Six     4      6
7   11 Class_11       7      1.000 Seven     1      7
8   12 Class_12       2      0.571   Two     1      8
9   12 Class_12       8      0.289 Eight     2      9
10  12 Class_12       1      0.140   One     3     10
11  13 Class_13       8      0.581 Eight     1     11
12  13 Class_13       4      0.210  Four     2     12
13  13 Class_13       2      0.112   Two     3     13
14  13 Class_13       3      0.079 Three     4     14
15  13 Class_13       5      0.018  Five     5     15

I have managed to go this far with the code.

cols<-c(One='Blue',Two='Red',Three='Yellow',Four='lightblue',Five='darkgrey',Six='Black', Seven='cyan',Eight='Green')
  

ggplot(Tx, aes(x=Class, y=Proportion, fill= Clu)) + 
  geom_col(width = .7, colour="black", lwd=0.1) +
  geom_text(aes(label=ifelse(Proportion >= 0.05, sprintf("%.2f",Proportion),"")),
            position=position_stack(vjust=0.5), colour="white") +
  coord_flip() +
  scale_y_continuous(labels = function(y) paste0(y))+
  scale_fill_manual(values = cols)+ 
  labs(y="", x="")

To summarize, I would like to have a graph with the proportions in increasing order for each class, but with the colors I specify for each clusters

enter image description here


Solution

  • One option (a little different than what you were thinking) is to use position.dodge and tidytext::reorder_within.

    library(tidyverse)
    library(tidytext)
    
    cols<-c('Blue','Red','Yellow','lightblue','darkgrey','Black', 'cyan', 'Green')
    
    Tx %>%
      mutate(Cluster2 = reorder_within(Cluster, Proportion, Class)) %>%
      ggplot(aes(Cluster2, Proportion, fill = as.factor(Cluster))) +
      geom_col(position = position_dodge2(preserve = "single")) +
      scale_x_reordered() +
      scale_fill_manual(values = cols) +
      coord_flip() +
      facet_grid(Class~., scales = 'free_y', space = 'free')
    
    

    enter image description here


    If you really need the stacked bars with the different orders, another option is to generate the plot for each class separately (which allows for the correct order) and then stack them all back together. This can be done using cowplot::plot_grid and cowplot::get_legend.

    Generate list of plots with correct order and stack them into one plot.

    library(tidyverse)
    library(cowplot)
    
    Tx2 <- Tx %>%
      mutate(Cluster = factor(Cluster))
    
    cols<-c(One='Blue',Two='Red',Three='Yellow',Four='lightblue',Five='darkgrey',Six='Black', Seven='cyan',Eight='Green')
    
    
    p_list <- lapply(unique(Tx2$Class), function(x){
      p <-  Tx2 %>%
        filter(Class == x) %>%
        ggplot(aes(Class, Proportion, fill = reorder(Clu, -Proportion))) +
        geom_col(color = 'black') +
        geom_text(aes(label=ifelse(Proportion >= 0.05, sprintf("%.2f",Proportion),"")),
                  position=position_stack(vjust=0.5), 
                  color = 'white') +
        coord_flip() +
        scale_fill_manual(values = cols) +
        labs(x = NULL, y = NULL) +
        theme_minimal() +
        theme(legend.position = 'none') 
      
     if (x != 'Class_13') p <- p + theme(axis.text.x = element_blank()) 
      
     p
    })
    
    
    
    p_col <- plot_grid(plotlist = p_list,
                       ncol = 1, 
                       align = 'v',
                       rel_heights = c(rep(1,4), 1.2))
    
    

    Generate the legend to be used.

    p <- ggplot(Tx2, aes(Class, Proportion, fill = reorder(Clu, as.numeric(Cluster)))) +  
      geom_col(color = 'black') +
      scale_fill_manual(values = cols, labels= 1:8, name = 'Cluster')
    l <- cowplot::get_legend(p)
    

    Put the stacked plots and the legend together.

    plot_grid(p_col, l, rel_widths = c(3, .4))
    

    !enter image description here


    Data

    Tx <- read.table(text = 
    '  Num    Class Cluster Proportion   Clu Order consec
    1    9  Class_9       2      0.859   Two     1      1
    2    9  Class_9       5      0.141  Five     2      2
    3   10 Class_10       2      0.622   Two     1      3
    4   10 Class_10       1      0.179   One     2      4
    5   10 Class_10       7      0.165 Seven     3      5
    6   10 Class_10       6      0.034   Six     4      6
    7   11 Class_11       7      1.000 Seven     1      7
    8   12 Class_12       2      0.571   Two     1      8
    9   12 Class_12       8      0.289 Eight     2      9
    10  12 Class_12       1      0.140   One     3     10
    11  13 Class_13       8      0.581 Eight     1     11
    12  13 Class_13       4      0.210  Four     2     12
    13  13 Class_13       2      0.112   Two     3     13
    14  13 Class_13       3      0.079 Three     4     14
    15  13 Class_13       5      0.018  Five     5     15')