1

I want to use ggplot2 to produce a graph like the one below, where the area between the line and a reference value is shaded, and the color of the shading depends on whether y is greater or less than the reference value.

CDC graph of 2020 primary and secondary syphilis cases relative to 2019 cases by reporting week

Here's a reprex using the LakeHuron dataset, setting 579 as the reference value, and using geom_ribbon() to do the shading. This gets close, but I only want to fill the area between the line and the reference line.

huron <- data.frame(year = 1875:1972, level = as.vector(LakeHuron))

ggplot(data = huron, aes(x = year)) + 
  geom_ribbon(aes(ymin = 579, ymax = level, fill = level > 579)) + 
  geom_line(aes(y = level)) +
  geom_hline(yintercept = 579)

huron example

Does anybody have any ideas? Thanks in advance.

Andrew T
  • 95
  • 1
  • 6

2 Answers2

3

A second option would be to use ggh4x::stat_difference which IMHO is one of the hidden gems of the ggh4x package:

huron <- data.frame(year = 1875:1972, level = as.vector(LakeHuron))

library(ggplot2)
library(ggh4x)

ggplot(huron, aes(x = year)) +
  ggh4x::stat_difference(aes(ymin = 579, ymax = level), levels = c("Above average", "Below Average")) +
  geom_line(aes(y = level)) +
  geom_hline(yintercept = 579) +
  scale_fill_manual(limits = c("Above average", "Below Average"), values = rev(scales::hue_pal()(2))) +
  labs(fill = NULL)

stefan
  • 90,330
  • 6
  • 25
  • 51
1

You need two ribbons if you want two different fills:

ggplot(data = huron, aes(x = year)) + 
  geom_ribbon(aes(ymin = 579, ymax = ifelse(level > 579, level, 579),
                  fill = "Above average")) +
  geom_ribbon(aes(ymax = 579, ymin = ifelse(level > 579, 579, level),
                  fill = "Below average")) +
  geom_line(aes(y = level)) +
  geom_hline(yintercept = 579) +
  scale_fill_manual(values = c("#86b2d8", "#bf311a"), name = NULL) +
  theme_classic(base_size = 16)

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • Ah, that makes sense. Thank you! While this definitely does what I was looking for, the ribbons still end up outside the line in places where it crosses the reference line. Any thoughts on how to clean up that overlap? – Andrew T Jul 13 '22 at 21:15