Search code examples
rggplot2fillgeom-bar

How to use fill in ggplot with two sets of factor variables?


I am plotting a stacked bar plot. For example:

library(data.table)
library(ggplot2)

dt <- data.table(category = c("A", "B", "C", "D", "A", "B", "C", "D"),
             variable = c("X", "X", "X", "X", "Y", "Y", "Y", "Y"),
             value = c(7, 5, 4, 2, 1, 1, 2, 1))
dt$variable <- factor(dt$variable, levels = c("Y", "X"))


p <- ggplot(dt, aes(x=category, y=value, fill=category)) +
  geom_bar(stat="identity", colour = "black") +
  coord_flip() +
  scale_fill_brewer(palette="Set3") +
  theme_bw() +
  theme(
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.margin=unit(c(0.8,0.8,0.8,0.8),"cm"),
text = element_text(size=14)
  )
p

I am trying to get each category where variable is "A" to be a different colour and all bars where variable in "B" to be black. Does anyone know how to do this? Is there a way to use fill for both category and variable?

I have tried:

dt$col <- ifelse(dt$variable == "Y", "black", dt$category)
dt$col <- factor(dt$col, levels = c("black","A", "B", "C", "D"))

p <- ggplot(dt, aes(x=category, y=value, fill=col)) +
  geom_bar(stat="identity", colour = "black") +
  coord_flip() +
  scale_fill_brewer(palette="Set3") +
  theme_bw() +
  theme(
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.margin=unit(c(0.8,0.8,0.8,0.8),"cm"),
text = element_text(size=14)
  )
p

However, I can not seem use scale_fill_brewer() and set the color to black.


Solution

  • I think you were on the right track with your second attempt. It's best to create a new column col just the way you did.

    The trick then is to make a custom color scale and color map, which explicitly maps the value "black" to the color "black". This custom color map can then be fed into scale_fill_manual.

    Does this look like your desired result?

    library(data.table)
    library(ggplot2)
    library(RColorBrewer)
    
    dt <- data.table(category = c("A", "B", "C", "D", "A", "B", "C", "D"),
                     variable = c("X", "X", "X", "X", "Y", "Y", "Y", "Y"),
                     value = c(7, 5, 4, 2, 1, 1, 2, 1))
    dt$variable <- factor(dt$variable, levels = c("Y", "X"))
    
    all_levels <- c(unique(dt$category), "black")
    dt$col <- ifelse(dt$variable == "Y", "black", dt$category)
    dt$col <- factor(dt$col, levels = all_levels)
    
    color_scale <- c(
      brewer.pal(length(all_levels) - 1, name = "Set3"),
      "black"
    )
    color_map <- setNames(color_scale, all_levels)
    
    ggplot(dt, aes(x=category, y=value, fill=col)) +
      geom_bar(stat="identity", colour = "black") +
      scale_fill_manual(values = color_map) +
      theme_bw() +
      theme(
        legend.position = "none",
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        plot.margin=unit(c(0.8,0.8,0.8,0.8),"cm"),
        text = element_text(size=14)
      )
    

    Created on 2019-09-04 by the reprex package (v0.3.0)