rggplot2fillgeom-bar

GGplot Legend Showing Blank Fill for Missing Factor Level


I am trying to use ggplot to plot a frequency plot where the dropped values still show in the legend. I have been able to achieve this by setting drop = FALSE in scale_fill_manual, however the dropped field has no color in the legend even though I have explicitly set it. I have created an example similar to my data below. In this example I am looking for Male to show in the legend with the color set in scale_fill_manual, but I cannot seem to figure out how to do this.

Data_Sample <- data.frame(CatType = c(rep("C1",3), rep("C2",7), rep("C3",8), rep("C4", 9), rep("C5", 2), rep("C6", 1)), 
Owner = c("Angela", "Karen", "Angela")) %>%
  mutate(Sex = ifelse(str_detect(CatType,"6"), "Male", "Female"),
         CatID = str_pad(row_number(), 2, pad="0"),
         Sex = factor(Sex, c("Male" = "Male", "Female" = "Female")),
         Owner = factor(Owner, c("Angela" = "Angela", "Karen" = "Karen")),
         CatType = factor(CatType))

Sample_Plot <- ggplot(Data_Sample %>% filter(., Owner == "Karen"), aes(x = CatType)) +
  geom_bar(colour = "#2E312F",aes(y = 100*((..count..)/sum(..count..)), fill = Sex)) +
  scale_x_discrete(drop = FALSE) +
  geom_text(aes(label = paste(stat(100*round((..count..)/sum(..count..),5)), "%", sep = ""), x = CatType, y = stat(100*round((..count..)/sum(..count..),5))), stat = "count", colour = "#2E312F",size = 3.5, fontface = "bold", vjust = -0.25) +
  theme(axis.text.x = element_text(face="bold"),
        axis.title.y = element_text(face="bold", size=11),
        axis.text.y = element_text(face="bold"),
        axis.title.x = element_text(face="bold", size=11),
        legend.title = element_text(face="bold", size=11),
        legend.text = element_text(face="bold", size=7.5),
        plot.title = element_text(face="bold", size=14, hjust = 0.5),
        legend.title.align = 0.5,
        legend.text.align = 0,
        legend.box.background = element_rect(colour = "black", fill = "transparent", size = 1.5),
        strip.text = element_text(face="bold", size=14),
        panel.border = element_rect(colour = "black", fill=NA, linewidth = 2)
  ) +
  scale_fill_manual(values = c("Female" = "#c90076", "Male" = "#2986cc"), drop = FALSE) +
  guides(fill = guide_legend(reverse=TRUE)) +
  labs(x = "Cat Type", y = "Percentage of Cats", title = paste("Karen's Cats", sep =" ")) +
  scale_y_continuous(label=scales::label_percent(scale = 1), limits = c(0,100))

enter image description here


Solution

  • This is due to a change introduced in ggplot2 3.5.0 which now requires to explicitly add show.legend=TRUE to a geom to display a legend key for unused factor levels.

    This is required both when using drop=FALSE or when setting the categories to be displayed using limits=.

    Additionally note that I switched to after_stat as .. and stat are deprecated as of version 3.4.0 and use hjust to align the legend text and title as legend.xxx.align was deprecated in 3.5.0.

    library(ggplot2)
    library(dplyr, warn = FALSE)
    
    ggplot(Data_Sample %>% filter(., Owner == "Karen"), aes(x = CatType)) +
      geom_bar(colour = "#2E312F", aes(
        y = 100 * after_stat(count / sum(count)),
        fill = Sex
      ), show.legend = TRUE) +
      scale_x_discrete(drop = FALSE) +
      geom_text(
        aes(
          label = paste0(round(100 * after_stat(count / sum(count))), "%"),
          x = CatType,
          y = 100 * after_stat(count / sum(count))
        ),
        stat = "count", colour = "#2E312F",
        size = 3.5, fontface = "bold", vjust = -0.25
      ) +
      theme(
        axis.text.x = element_text(face = "bold"),
        axis.title.y = element_text(face = "bold", size = 11),
        axis.text.y = element_text(face = "bold"),
        axis.title.x = element_text(face = "bold", size = 11),
        legend.title = element_text(face = "bold", size = 11, hjust = .5),
        legend.text = element_text(face = "bold", size = 7.5, hjust = 0),
        plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
        legend.box.background = element_rect(
          colour = "black",
          fill = "transparent", linewidth = 1.5
        ),
        strip.text = element_text(face = "bold", size = 14),
        panel.border = element_rect(colour = "black", fill = NA, linewidth = 2)
      ) +
      scale_fill_manual(values = c("Female" = "#c90076", "Male" = "#2986cc"), drop = FALSE) +
      guides(fill = guide_legend(reverse = TRUE)) +
      labs(x = "Cat Type", y = "Percentage of Cats", title = paste("Karen's Cats", sep = " ")) +
      scale_y_continuous(label = scales::label_percent(scale = 1), limits = c(0, 100))