5

In this bar chart for the specific viridis option is it possible to set of color which can show the numbers inside the chart clear 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")
Henrik
  • 65,555
  • 14
  • 143
  • 159
foc
  • 947
  • 1
  • 9
  • 26
  • Related: [ggplot2: text color based on background contrast](https://stackoverflow.com/questions/47281365/ggplot2-text-color-based-on-background-contrast); [Is there a is light or is dark color function in R?](https://stackoverflow.com/questions/49437263/is-there-a-is-light-or-is-dark-color-function-in-r); [Changing background color for a text annotation to increase contrast and visibility](https://stackoverflow.com/questions/39602828/changing-background-color-for-a-text-annotation-to-increase-contrast-and-visibil) – Henrik Jul 08 '20 at 18:13

2 Answers2

8

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")
stefan
  • 90,330
  • 6
  • 25
  • 51
  • Great solution, and it does not mess up the positioning like `geom_label` or `ggtext::geom_textbox` tend to do with `position_stack`... – user12728748 Jul 08 '20 at 19:04
3

Try this:

Replace geom_text(...) with geom_label(fill = "white", ...) which automatically provides a label "box".


library(ggplot2)

ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
  geom_bar(stat = "identity") +
  geom_label(size = 3, position = position_stack(vjust = 0.5), fill = "white") +
  scale_fill_viridis_d(option  = "magma")

Created on 2020-07-08 by the reprex package (v0.3.0)

Peter
  • 11,500
  • 5
  • 21
  • 31