rggplot2colorsgeom-bar

How can I set a text color to always contrast with the fill color of a chart?


In this bar chart for the specific viridis option is it possible to set of color which can show the numbers inside the chart clearly even for the darker option of the scale?

library(ggplot2)
Year      <- c(rep(c("2006-07", "2007-08", "2008-09", "2009-10"), each = 4))
Category  <- c(rep(c("A", "B", "C", "D"), times = 4))
Frequency <- c(168, 259, 226, 340, 216, 431, 319, 368, 423, 645, 234, 685, 166, 467, 274, 251)
Data      <- data.frame(Year, Category, Frequency)
ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
  geom_bar(stat = "identity") +
  geom_text(size = 3, position = position_stack(vjust = 0.5)) +  scale_fill_viridis_d(option  = "magma")

Solution

  • Making use of a trick I learned from scales::show_col you can choose the text color automatically according to the fill like so:

    library(ggplot2)
    Year      <- c(rep(c("2006-07", "2007-08", "2008-09", "2009-10"), each = 4))
    Category  <- c(rep(c("A", "B", "C", "D"), times = 4))
    Frequency <- c(168, 259, 226, 340, 216, 431, 319, 368, 423, 645, 234, 685, 166, 467, 274, 251)
    Data      <- data.frame(Year, Category, Frequency)
    
    # Trick from scales::show_col
    hcl <- farver::decode_colour(viridisLite::magma(length(unique(Category))), "rgb", "hcl")
    label_col <- ifelse(hcl[, "l"] > 50, "black", "white")
    
    ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
      geom_bar(stat = "identity") +
      geom_text(aes(color = Category), size = 3, position = position_stack(vjust = 0.5), show.legend = FALSE) +  
      scale_color_manual(values = label_col) +
      scale_fill_viridis_d(option  = "magma")
    

    EDIT

    A second option which I learned recently would be to make use of ggplot2::after_scale and prismatic::best_contrast to automatically pick the text color with best contrast like so:

    library(prismatic)
    
    ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
      geom_bar(stat = "identity") +
      geom_text(aes(color = after_scale(
        prismatic::best_contrast(fill, c("white", "black"))
      )),
      size = 3, position = position_stack(vjust = 0.5), show.legend = FALSE
      ) +
      scale_fill_viridis_d(option = "magma")