Search code examples
rggplot2bar-chartfillgeom-bar

Always use the same fill colors barplot ggplot2 in R


For different products I am trying to plot the sales per saleschannel in a stacked barplot using ggplot in R. I plotted the products on the x-axis and the amounts plotted on the y-axis. The fill is the channels.

My code looks like this:

ggplot(df, aes(x=products, y=sales, fill=channel)) + geom_bar(stat = "identity")

There are a total of 4 different saleschannels (website, email, street sales, direct message). The problem I face is that for different products different plot colors are used, because not all products have used each saleschannel for sales. Important to know is that I want to plot each product in a separate plot. For example, product1 has used all four saleeschannels and will thus use four different colors in the stacked barplot. However, product2 only used three saleschannels (website, email and street sales), and will thus use three different colors in the stacked barplot. Because of this, R uses different colors for the products for plots with products that use four channels.

Is there a way that I can always use the same colors no matter how many saleschannels a product uses?

edit: I added the code as was requested, see below:

products <- c("product1","product1","product1","product1","product1","product1","product1",
          "product2","product2","product2","product2","product2","product2",
          "product3","product3","product3","product3","product3","product3","product3",
          "product4","product4","product4","product4","product4","product4","product4","product4")

sales <- c(5,12,14,21,8,9,11,2,5,6,8,19,21,13,14,5,22,19,17,13,12,10,8,6,9,11,15,16)
saleschannel <- c("website","website","website","email","street sales","direct message","direct message",
              "website","website","email","email","street sales","street sales",
              "website","website","email","email","street sales","street sales","direct message",
              "website","website","email","email","street sales","street sales","direct message","direct message")

df <- data.frame(products, sales, saleschannel)
df_1 <- subset(df, products=="product1")
df_2 <- subset(df, products=="product2")

plot_product1 <- ggplot(df_1, aes(x=products, y=sales, fill=saleschannel)) +  geom_bar(stat = "identity")

plot_product2 <- ggplot(df_2, aes(x=products, y=sales, fill=saleschannel)) +  geom_bar(stat = "identity")

library(cowplot)
plot_grid(plot_product1, plot_product2)

This gets me the following plots:

as per data and code getting this plot


Solution

  • You can use scale_fill_manual(values = ...), together with preset colors for each saleschannel.

    First, you need to get the explicit names of the colors. You can use any other colors you like, for example the packages RColorBrewer has some options. Here, I use the ggplot2 default, like shown in this SO answer:

    # n: number of colors to generate
    gg_color_hue <- function(n) {
      hues = seq(15, 375, length = n + 1)
      hcl(h = hues, l = 65, c = 100)[1:n]
    }
    

    Next, define a color for each sales channel by creating a named string vector, where the names are the saleschannel levels and the values are the colors:

    s = unique(saleschannel)
    cols = setNames(gg_color_hue(length(s)), s)
    cols
    ##       website          email   street sales direct message 
    ##     "#F8766D"      "#7CAE00"      "#00BFC4"      "#C77CFF" 
    

    Add explicit color scales to your plots. In your case, the aestetic of choice is fill, therefore you need scale_fill_manual. You can also add + scale_fill_manual(...) to the end of your definitions of plot_productX:

    plot_product1 <- plot_product1 + scale_fill_manual(values = cols)
    plot_product2 <- plot_product2 + scale_fill_manual(values = cols)
    

    Then use plot_grid (an alternative is gridExtra::grid.arrange) as in your question

    plots with same color scale