Search code examples
rggplot2plotlydata-visualizationbar-chart

How to plot negative bars with same coloured shapes as positive ones?


My df has two numerical variables (positive and negative values) and 2 categorical variables. since I want to plot negative bars with same coloured shape/boundaries I specify colours manually in the dataframe and use appropriate code. My question is how to avoid specifying colours in advance and let R to use random colors but keep the white space inside the negative bars?

df <- data.frame(model  = c("A","B","C","D","B","C"),
                  category = c("origin", "origin","origin","abroad","abroad","abroad"),
                 pos = c(40,50,45,100,105,80),
                 neg = c(-10,-5,-4,-16,-7,-2),
                 Colour = c("chocolate","deeppink4","yellow","steelblue3","deeppink4","yellow"))

Colour <- as.character(df$Colour)
Colour <- c(Colour,"white")
names(Colour) <- c(as.character(df$model),"white")



df <- df %>% pivot_longer(., cols=c('pos','neg'),
                           names_to = 'sign') %>% 
  mutate(Groups = paste(category, model),
         sign = factor(sign, levels = c("neg", "pos")))



bar2 <- ggplot(df, aes(value, tidytext::reorder_within(model, value, category),
                fill = ifelse(sign == "neg", "white", model), 
                color = model))+
  geom_col(position = "stacked") +
  scale_fill_manual(values = Colour, breaks = df$model) +
  scale_color_manual(values = Colour, breaks = df$model) +
  tidytext::scale_y_reordered() +
  labs(fill = "model") +
  facet_grid(category ~ ., switch = "y",scales = "free_y") +
  theme(axis.text.x = element_text(angle = 90),
        strip.background = element_rect(fill = "white"),
        strip.placement = "outside",
        strip.text.y.left = element_text(angle = 0),
        panel.spacing = unit(0, "lines"))+theme(legend.position="none") + 
        labs( title = "BarPlot",
              subtitle = "changes",
              y = " ") 

bar2

enter image description here


Solution

  • Plot positive and negative bars as separate layers:

    ggplot() +
      # plot positive with fill and colour based on model
      geom_col(aes(value, tidytext::reorder_within(model, value, category),
                   fill = model, color = model), 
               data = df[df$sign == "pos", ],
               position = "stack") +
      # plot negative with colour from based on model, but fill fixed as "white"
      geom_col(aes(value, tidytext::reorder_within(model, value, category),
                   color = model), 
               data = df[df$sign == "neg", ], 
               fill = "white",
               position = "stack") +
      # the rest is same as OP's code
      tidytext::scale_y_reordered() +
      labs(fill = "model") +
      facet_grid(category ~ ., switch = "y",scales = "free_y") +
      theme(axis.text.x = element_text(angle = 90),
            strip.background = element_rect(fill = "white"),
            strip.placement = "outside",
            strip.text.y.left = element_text(angle = 0),
            panel.spacing = unit(0, "lines")) + 
      theme(legend.position="none") + 
      labs( title = "BarPlot",
            subtitle = "changes",
            y = " ") 
    

    enter image description here