1

I have some data that I plot using the geom_polygon in ggplot (data pasted at the end).

# GGplot: anomaly for ONI region 3.4 --------------------------------------
library(ggplot2)
library(tidyverse)
ggplot(data = onisoiquimalypense3, mapping = aes(x = date, y = value)) + 
  geom_polygon(linetype = 1, fill = "darkgray", alpha= 1) + 
  theme_bw() + 
  geom_abline(intercept = c(-0.5, 0.5), slope = 0,linetype = 2) + 
  xlab("Year") +
  ylab("3-Month Oceanic Niño Index (ONI)")    

Which gives enter image description here

I'd like to have parts of the area of the polygon to be coloured red if the polygon crosses 0.5 (dashed line) and colour blue if it crosses -0.5 (see the pointed parts).

Something like this, but for ggplot. Is there an automatic way to set that in ggplot?

Data

## Reading data ------------------------------------------------------------
# Data from https://origin.cpc.ncep.noaa.gov/products/analysis_monitoring/ensostuff/ONI_v5.php
onisoiquimalypense3 = structure(list(Year = c(2016L, 2016L, 2016L, 2016L, 2016L, 2016L, 
                                              2016L, 2016L, 2016L, 2016L, 2016L, 2016L, 2016L, 2017L, 2017L, 
                                              2017L, 2017L, 2017L, 2017L, 2017L, 2017L, 2017L, 2017L, 2017L, 
                                              2017L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 
                                              2018L, 2018L, 2018L, 2018L, 2019L, 2019L, 2019L, 2019L, 2019L, 
                                              2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2020L, 2020L, 
                                              2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 
                                              2020L, 2021L, 2021L, 2021L, 2021L, 2021L, 2021L, 2021L, 2021L, 
                                              2021L, 2021L, 2021L, 2021L, 2022L, 2022L, 2022L, 2022L, 2022L, 
                                              2022L, 2022L, 2022L, 2022L, 2022L, 2022L, 2022L, 2022L, 2022L
), 
name = c("DJF", "DJF", "JFM", "FMA", "MAM", "AMJ", "MJJ", 
         "JJA", "JAS", "ASO", "SON", "OND", "NDJ", "DJF", "JFM", "FMA", 
         "MAM", "AMJ", "MJJ", "JJA", "JAS", "ASO", "SON", "OND", "NDJ", 
         "DJF", "JFM", "FMA", "MAM", "AMJ", "MJJ", "JJA", "JAS", "ASO", 
         "SON", "OND", "NDJ", "DJF", "JFM", "FMA", "MAM", "AMJ", "MJJ", 
         "JJA", "JAS", "ASO", "SON", "OND", "NDJ", "DJF", "JFM", "FMA", 
         "MAM", "AMJ", "MJJ", "JJA", "JAS", "ASO", "SON", "OND", "NDJ", 
         "DJF", "JFM", "FMA", "MAM", "AMJ", "MJJ", "JJA", "JAS", "ASO", 
         "SON", "OND", "NDJ", "DJF", "JFM", "FMA", "MAM", "AMJ", "MJJ", 
         "JJA", "JAS", "ASO", "SON", "OND", "NDJ", "NDJ", "NDJ"), 
value = c(0, 
          2.5, 2.1, 1.6, 0.9, 0.4, -0.1, -0.4, -0.5, -0.6, -0.7, -0.7, 
          -0.6, -0.3, -0.2, 0.1, 0.2, 0.3, 0.3, 0.1, -0.1, -0.4, -0.7, 
          -0.8, -1, -0.9, -0.9, -0.7, -0.5, -0.2, 0, 0.1, 0.2, 0.5, 0.8, 
          0.9, 0.8, 0.7, 0.7, 0.7, 0.7, 0.5, 0.5, 0.3, 0.1, 0.2, 0.3, 0.5, 
          0.5, 0.5, 0.5, 0.4, 0.2, -0.1, -0.3, -0.4, -0.6, -0.9, -1.2, 
          -1.3, -1.2, -1, -0.9, -0.8, -0.7, -0.5, -0.4, -0.4, -0.5, -0.7, 
          -0.8, -1, -1, -1, -0.9, -1, -1.1, -1, -0.9, -0.8, -0.9, -1, -1, 
          -0.9, -0.8, 0, 0),
month.mean = c("1", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", 
               "6", "7", "8", "9", "10", "11", "12", "12", "12"), 
date = structure(c(16801, 
                   16801, 16832, 16861, 16892, 16922, 16953, 16983, 17014, 17045, 
                   17075, 17106, 17136, 17167, 17198, 17226, 17257, 17287, 17318, 
                   17348, 17379, 17410, 17440, 17471, 17501, 17532, 17563, 17591, 
                   17622, 17652, 17683, 17713, 17744, 17775, 17805, 17836, 17866, 
                   17897, 17928, 17956, 17987, 18017, 18048, 18078, 18109, 18140, 
                   18170, 18201, 18231, 18262, 18293, 18322, 18353, 18383, 18414, 
                   18444, 18475, 18506, 18536, 18567, 18597, 18628, 18659, 18687, 
                   18718, 18748, 18779, 18809, 18840, 18871, 18901, 18932, 18962, 
                   18993, 19024, 19052, 19083, 19113, 19144, 19174, 19205, 19236, 
                   19266, 19297, 19327, 19327, 19327), class = "Date"), 
elnlan = c("El Nino", 
           "El Nino", "El Nino", "El Nino", "El Nino", "none", "none", "none", 
           "none", "La Nina", "La Nina", "La Nina", "La Nina", "none", "none", 
           "none", "none", "none", "none", "none", "none", "none", "La Nina", 
           "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", "none", 
           "none", "none", "none", "none", "none", "El Nino", "El Nino", 
           "El Nino", "El Nino", "El Nino", "El Nino", "El Nino", "none", 
           "none", "none", "none", "none", "none", "none", "none", "none", 
           "none", "none", "none", "none", "none", "none", "La Nina", "La Nina", 
           "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", 
           "La Nina", "none", "none", "none", "none", "La Nina", "La Nina", 
           "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", 
           "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", "La Nina", 
           "La Nina", "La Nina", "La Nina", "La Nina")), row.names = c(NA, 
                                                                       -87L),
class = c("tbl_df", "tbl", "data.frame"))

Edit

I did try some solutions and the closest I could get is getting 3 layers.

my.lev = .5
my.lev2 = -.5
ggplot(data = onisoiquimalypense3, mapping = aes(x = date, y = value)) + 
  # geom_area(aes(fill=col)) +
  geom_polygon(linetype = 1, fill = "darkgray", alpha= 1) + 
    geom_ribbon(aes(
      ymin = my.lev, ymax = ifelse(value >= my.lev, value, my.lev),
    ), fill = "blue") +
    geom_ribbon(aes(
      ymax = my.lev2, ymin = ifelse(value >= my.lev2, my.lev2, value)
    ), fill = "red") +
  theme_bw() + 
  geom_abline(intercept = c(-0.5, 0.5), slope = 0,linetype = 2) + 
  xlab("Year") +
  ylab("3-Month Oceanic Niño Index (ONI)")  

But for some reason, the alignment between the 2 polygons (see black circles in the attached image) is not great. Is there a way to make it better looking (overlapping: so that only the colour changes)

enter image description here

M. Beausoleil
  • 3,141
  • 6
  • 29
  • 61
  • 1
    This may help, particularly the first answer using `ggh4x::stat_difference()`: [How to fill with different colors between two lines?](https://stackoverflow.com/questions/27135962/how-to-fill-with-different-colors-between-two-lines-originally-fill-geom-poly) – zephryl Apr 15 '23 at 00:33
  • I wasn't able to make it work with ggh4x, maybe you have an idea? When I used `[...] ggh4x::stat_difference(aes(ymin = c(-0.5), ymax = value)) + geom_line(aes(y = value)) + [...]` I got this Error in `ggh4x::stat_difference()`: ! Problem while converting geom to grob. ℹ Error occurred in the 2nd layer. Caused by error in `draw_group()`: ! Aesthetics can not vary along a ribbon Run `rlang::last_error()` to see where the error occurred. – M. Beausoleil Apr 15 '23 at 00:59

1 Answers1

3

The issue you mentioned in the comment when using ggh4x::stat_difference is that your data contains some duplicated (start and end) dates (which you probably added for the polygon). Drop these for ggh4x::stat_difference() and the code should work fine. Additionally I added the adjustments to get right colors:

library(ggplot2)
library(ggh4x)

my.lev <- .5
my.lev2 <- -.5

# Drop duplicated values per date
onisoiquimalypense4 <- onisoiquimalypense3 |>
  dplyr::add_count(date) |>
  dplyr::filter(n == 1 | abs(value) > 0)

ggplot(data = onisoiquimalypense3, mapping = aes(x = date, y = value)) +
  geom_polygon(linetype = 1, fill = "darkgray", alpha = 1) +
  ggh4x::stat_difference(data = onisoiquimalypense4, aes(
    ymin = my.lev, ymax = value,
    fill = after_stat(dplyr::if_else(sign != "+", NA, sign))
  )) +
  ggh4x::stat_difference(data = onisoiquimalypense4, aes(
    ymin = my.lev2, ymax = value,
    fill = after_stat(dplyr::if_else(sign != "-", NA, sign))
  )) +
  scale_fill_manual(values = c("-" = "red", "+" = "blue"), na.value = "transparent", guide = "none") +
  theme_bw() +
  geom_abline(intercept = c(-0.5, 0.5), slope = 0, linetype = 2) +
  xlab("Year") +
  ylab("3-Month Oceanic Niño Index (ONI)")

enter image description here

stefan
  • 90,330
  • 6
  • 25
  • 51