3

I'm trying to change the color of the individual bars of my ggplot using scale_fill_manual, but am having trouble because the bars are within group (here "condition"). How can I overcome this issue?

Here is my dataset:

df <- data.frame(
"condition" = c("A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B"), 
"type" = c("Apple", "Apple", "Apple", "Apple", "Pear", "Pear", "Pear", "Pear", "Orange", "Orange", "Orange", "Orange", "Tomato", "Tomato", "Tomato", "Tomato", "Tomato", "Berry", "Berry", "Berry"), 
"ripeness" = c("bad","bad", "good", "good", "bad", "bad", "good", "good", "bad","bad", "good", "good", "bad", "bad", "good", "good", "bad","bad", "good", "good"), 
count = c(19, 4, 7, 2, 7, 7, 1, 13, 16, 16, 31, 13, 30, 12, 39, 17, 1, 36, 4, 27)
)

and the code that I am using to plot it:

plot <- ggplot(df, aes(x=type, y=count, fill=ripeness)) +
  geom_bar(stat="summary", width = .7, position = position_dodge(width = .75)) +
  theme_light()

#the below is non-working code
plot + scale_fill_manual(values=c("black","blue","black","green","black","stripes-2","black","stripes-3","black", "yellow"))

How can I specify the colors of the individual bars? For explanatory purposes, I wrote in color names but will swap these out for html color codes. Later, I am also aiming to texture two of my bars using ggpattern (https://coolbutuseless.github.io/package/ggpattern/articles/geom-gallery-geometry.html#colour-example-1) but that doesn't need to be solved in this post, if too difficult.

I am trying to recreate this plot in r:

enter image description here

psychcoder
  • 543
  • 3
  • 14

2 Answers2

2

One way would be to add a column of colors in the dataframe and use scale_fill_identity.

library(dplyr)
library(ggplot2)

colors <- c("black","blue","black","green","black","orange",
            "black","pink","black", "yellow")

df %>%
  distinct(type, ripeness) %>%
  mutate(color = colors) %>%
  inner_join(df, by = c('type', 'ripeness')) %>%
  ggplot(aes(x=type, y=count, fill=color)) +
  geom_bar(stat="summary", width = .7, position = position_dodge(width = .75)) +
  theme_light() +
  scale_fill_identity()

enter image description here

PS - I couldn't make "stripes-2" and "stripes-3" color work so changed it to other random color.

Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • — I can open another post, but I wonder I can ask if you might have advice about how to insert a pdf or png as the fill to one of the bars? That's one solution I'm debating for stripes. – psychcoder Feb 01 '21 at 07:49
  • 1
    I see using png is possible with `ggpattern` or other packages but I haven't used it. – Ronak Shah Feb 01 '21 at 07:59
0

This is a way to get also the two legends using two aesthetics (color and fill). Even if it is possible to reproduce the plot in {ggplot2}, using two aesthetics does not help to visualize data better in my opinion.

library(ggplot2)
library(tidyverse)

df <- data.frame(
"condition" = c("A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", 
  "A", "B", "A", "B", "A", "B", "A", "B"), 
"type" = c("Apple", "Apple", "Apple", "Apple", "Pear", "Pear", "Pear", "Pear", 
  "Orange", "Orange", "Orange", "Orange", "Tomato", "Tomato", "Tomato", 
  "Tomato", "Tomato", "Berry", "Berry", "Berry"), 
"ripeness" = c("bad","bad", "good", "good", "bad", "bad", "good", "good", 
  "bad","bad", "good", "good", "bad", "bad", "good", "good", "bad","bad",
  "good", "good"), 
count = c(19, 4, 7, 2, 7, 7, 1, 13, 16, 16,
  31, 13, 30, 12, 39, 17, 1, 36, 4, 27)
)

# to get the same order of types as in the given Figure
df$type <- factor(df$type, 
  levels = c("Apple", "Pear", "Orange", "Tomato", "Berry"))
df |> 
  ggplot() +
  # first, plot the bars for "good" and map type to fill aesthetics
  # Change width and position to that that corresponds to good when fill is 
  # mapped to ripeness
  geom_bar(data = df |> filter(ripeness == "good"), 
    aes(x=type, y=count, fill = type),
    stat="summary", fun = mean,
    position = position_nudge(x = 0.225), width = 0.45, color = "white") +
  # second, plot the bars for "bad" and map ripeness to color asthetics
  # Change width and position to that that corresponds to good when fill is 
  # mapped to ripeness. We need to use a different aesthetics to get two
  # legends
  geom_bar(data = df |> filter(ripeness == "bad"), 
    aes(x=type, y=count, color = ripeness), 
    stat="summary", fun = mean,
    position = position_nudge(x = -0.225), width = 0.45) +
  # Legend for the color aesthetics (with no name)
  scale_color_manual(name = "", values = "white", breaks = "bad") +
  # Legend for the fill aesthetics
  # Colors for the bars are given in the paramters values
  scale_fill_manual(name = "good", values = hcl.colors(5), 
    breaks = levels(df$type)) +
  # Reproduce the axis in the figure
  scale_y_continuous(breaks = seq(0, 35, by = 5), limits = c(0,40), 
    expand = c(0,0)) + 
  scale_x_discrete(expand = c(0.13,0)) + 
  labs(x = "", y = "frequency") +
  theme_classic() +
  # Avoid ticks in x axis as in the example
  theme(axis.ticks.x = element_blank())

Created on 2022-01-23 by the reprex package (v2.0.1)

josep maria porrà
  • 1,198
  • 10
  • 18