Search code examples
rggplot2ggrepel

Text color with geom_label_repel


Not specific to any particular piece of code, is there a relatively straightforward way to change the color of the text in a geom_label_repel box?

Specifically, I have code that produces the below chart

enter image description here

The percentage in the label box is the percent change in 7-day moving average for the most recent week over the week prior. I'd simply like to color the text red when the value is positive and green when it is negative.

The dataframe for this chart can be copied from here.

The plot code is

#endpoint layer
BaseEndpoints <- smDailyBaseData %>% filter(Base %in% AFMCbases) %>%
  group_by(Base) %>%
  filter(DaysSince == max(DaysSince)) %>%
  select(Base, abbv, DaysSince, newRate,label) %>%
  ungroup()

ZoomEndpoints <- BaseEndpoints %>% filter(Base != 'Edwards') %>%
  mutate(zoom = TRUE)
CAEndPoint <- BaseEndpoints %>% filter(Base == 'Edwards') %>%
  mutate(zoom = FALSE)

ZoomEndpoints <- rbind(ZoomEndpoints, CAEndPoint)

BasePlot <- smDailyBaseData %>% filter(Base %in% AFMCbases) %>%
  ggplot(mapping = aes(x = as.numeric(DaysSince), y = newRate)) +
  geom_line(aes(color=abbv),show.legend = FALSE) +
  scale_color_ucscgb() +
 geom_point(data = BaseEndpoints,size = 1.5,shape = 21, 
            aes(color = abbv,fill = abbv), show.legend = FALSE) +
 geom_label_repel(data=ZoomEndpoints, aes(label=label), show.legend = FALSE,
                   vjust = 0, xlim=c(105,200), size=3, direction='y') +
  labs(x = "Days Since First Confirmed Case", 
       y = "% Local Population Infected Daily") +
  theme(plot.title = element_text(size = rel(1), face = "bold"),
        plot.subtitle = element_text(size = rel(0.7)),
        plot.caption = element_text(size = rel(1))) +
  facet_zoom(xlim = c(50,120), ylim=c(0,0.011),zoom.data=zoom)



print(BasePlot)

Solution

  • Here's a bit of a hacky solution since you can't have two scale_color_*'s at the same time:

    The approach centers on manually assigning the color outside of aes in the geom_label_repel call. Adding one to the grepl result that searches for the minus sign in the label allows you to subset the two colors. You need two colors for each label, I assume for the box and for the text, so I used rep.

    smDailyBaseData %>% 
    ggplot(mapping = aes(x = as.numeric(DaysSince), y = newRate)) +
      geom_line(aes(color=abbv),show.legend = FALSE) +
      scale_color_ucscgb() +
     geom_point(data = BaseEndpoints,size = 1.5,shape = 21, 
                aes(color = abbv,fill = abbv), show.legend = FALSE) +
     geom_label_repel(data=ZoomEndpoints, aes(label=label),
         color = rep(c("green","red")[1+grepl("\\-\\d",as.factor(ZoomEndpoints$label))],times = 2),
         show.legend = FALSE, vjust = 0, xlim=c(105,200),
         size=3, direction='y') +
     labs(x = "Days Since First Confirmed Case", 
           y = "% Local Population Infected Daily") +
     theme(plot.title = element_text(size = rel(1), face = "bold"),
            plot.subtitle = element_text(size = rel(0.7)),
            plot.caption = element_text(size = rel(1))) +
     facet_zoom(xlim = c(50,120), ylim=c(0,0.011),zoom.data=zoom)
    

    enter image description here

    Data Setup

    #source("https://pastebin.com/raw/Vn2abQ4a")
    BaseEndpoints <- smDailyBaseData %>% 
      group_by(Base) %>%
      dplyr::filter(DaysSince == max(DaysSince)) %>%
      dplyr::select(Base, abbv, DaysSince, newRate,label) %>%
      ungroup()
    ZoomEndpoints <- BaseEndpoints %>% filter(Base != 'Edwards') %>%
      mutate(zoom = TRUE)
    CAEndPoint <- BaseEndpoints %>% filter(Base == 'Edwards') %>%
      mutate(zoom = FALSE)
    ZoomEndpoints <- rbind(ZoomEndpoints, CAEndPoint)