5
votes

I have a stacked bar plot, labelled with geom_text. To increase the visibility of the labels, I want to set the label color to either white or black depending of the 'darkness' of their background, i.e. the fill color of the bars. Thus, darker bars should have a white label, and lighter bars should have a black label.

I started with code from the question Showing data values on stacked bar chart in ggplot2, the answer to the question Is there a is light or is dark color function in R?. On top of that I'd like to show the label of the data value in 'white' or 'black' depending whether the stacked bar fill color is dark or not dark respectively.

I made two attempts. The first is using aes(colour=...) of the geom_text function but this one fails ... why?.

The second attempt is using the function scale_colour_manual. But here the stacked bar lines are also "coloured" using the black or white toggling setting.

library(ggplot2)
library(RColorBrewer)

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)

isDark <- function(color) {
  (sum(grDevices::col2rgb(color) *c(299, 587,114))/1000 < 123)
}

## control the color assignments
paletteName <- 'Set1' # 'Dark2'
colorsPerCat <- brewer.pal(name=paletteName,n=4)
names(colorsPerCat) <- c("A", "B", "C", "D")

## First attempt
Data$LabelColor <- as.character(as.vector(sapply(unlist(colorsPerCat)[Data$Category], function(color) { if (isDark(color)) 'white' else 'black' })))
Data
p <- ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
  geom_bar(stat = "identity") +
  geom_text(aes(colour=LabelColor), size = 3, position = position_stack(vjust = 0.5)) +
  scale_fill_manual(values = colorsPerCat)
p

## Second attempt
labelColoursPerCat <- sapply(as.vector(colorsPerCat), function(color) { if (isDark(color)) 'white' else 'black' })
names(labelColoursPerCat) <- c("A", "B", "C", "D")
p <- ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency, colour = Category)) +
  geom_bar(stat = "identity") +
  geom_text(size = 3, position = position_stack(vjust = 0.5)) +
  scale_fill_manual(values = colorsPerCat) +
  scale_colour_manual(values = labelColoursPerCat)
p
1
p + scale_color_identity()?Henrik
@Henrik Sorry yes forgot :)SkyWalker
@Henrik I didn’t quite get what scale_color_identity is good got hereSkyWalker
When I looked at your "Data" in "First attempt", it seemed like you already had the actual colors intended for geom_text in a variable ("LabelColor") ("i.e. it already represents aesthetic values that ggplot2 can handle directly"; from ?scale_color_identity). See also Using a pre-defined color palette in ggplot and ggplot: colour points by groups based on user defined coloursHenrik

1 Answers

2
votes

Two ways to solve this issue by playing with geom_bar color or size:

Data$LabelColor <- as.factor(Data$LabelColor)
p <- ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency, colour = LabelColor)) +
              geom_bar(stat = "identity", color = "black") +
              geom_text(size = 3, position = position_stack(vjust = 0.5)) +
              scale_fill_manual(values = colorsPerCat) +  
              scale_colour_manual(values = levels(Data$LabelColor)) +
              guides(colour = FALSE)

enter image description here

Data$LabelColor <- as.factor(Data$LabelColor)
p <- ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency, colour=LabelColor)) +
              geom_bar(stat = "identity", size = 0) +
              geom_text(size = 3, position = position_stack(vjust = 0.5)) +
              scale_colour_manual(values = levels(Data$LabelColor)) +
              scale_fill_manual(values = colorsPerCat) +  
              guides(colour = FALSE)

enter image description here