Search code examples
rggplot2geom-text

Change `ggplot` label color by conditions


I have a question about changing color for geom_text & geom_label base on a condition.

Expected out put

  • Data label has color = blue for 2021, color = grey for 2020
  • Gap label is red when negative and green when positive

Data

data <-  tibble(Factor = c("A", "B", "A", "B"),
   Score = c(90, 85, 85, 90),
   Year = c("2020", "2020", "2021", "2021"))

Data transform

   df_2 = data %>% 
     pivot_wider(names_from = Year, names_prefix = "Year", values_from = Score) %>% 
     mutate(gap = Year2021 - Year2020) %>% 
     select(Factor, gap)

Plot

data %>% 
  left_join(df_2) %>% 
  ggplot(aes(x = Factor, y = Score, fill = Year)) +
  geom_col(position='dodge') +
  geom_text(aes(label=Score), 
            position=position_dodge(width = 0.9), 
            vjust=-0.40) +
  geom_label(aes(y = 100, label = ifelse(Year == 2021, gap, NA_character_)), na.rm = TRUE) +
  scale_y_continuous(limits = c(0,105))
  theme_minimal()

enter image description here

Expected out put

  • 85 & 90 = blue for 2021, 90 & 85 = grey for 2020
  • -5 = red and 5 = green

Solution

  • Second option would be to use a manual scale for the text and bar colors. For the labels we could use the ggnewscale package which allows for multiple scales for the same aesthetic. Doing so we could add a second fill scale:

    library(ggplot2)
    library(ggnewscale)
    
    ggplot(dat, aes(x = Factor, y = Score, fill = Year)) +
      geom_col(position = "dodge") +
      geom_text(aes(label = Score, color = Year),
        position = position_dodge(width = 0.9),
        vjust = -0.40
      ) +
      scale_color_manual(aesthetics = c("fill", "color"), values = c("2021" = "blue", "2020" = "grey")) +
      ggnewscale::new_scale_fill() +
      geom_label(aes(y = 100, label = ifelse(Year == 2021, gap, NA_character_), fill = gap > 0), na.rm = TRUE) +
      scale_fill_manual(values = c("FALSE" = "red", "TRUE" = "green")) +
      scale_y_continuous(limits = c(0, 105)) +
      theme_minimal()
    

    enter image description here

    DATA

    data <- data.frame(
      Factor = c("A", "B", "A", "B"),
      Score = c(90, 85, 85, 90),
      Year = c("2020", "2020", "2021", "2021")
    )
    
    library(dplyr)
    library(tidyr)
    
    df_2 <- data %>%
      pivot_wider(names_from = Year, names_prefix = "Year", values_from = Score) %>%
      mutate(gap = Year2021 - Year2020) %>%
      select(Factor, gap)
    
    dat <- data %>%
      left_join(df_2)