rggplot2geom-area

Displaying SPEI with ggplot: geom_area ends/starts at different positions when sign changes


Here's some data

structure(list(Period = structure(c(2017.83333333333, 2017.91666666667, 
2018, 2018.08333333333, 2018.16666666667, 2018.25, 2018.33333333333, 
2018.41666666667, 2018.5, 2018.58333333333, 2018.66666666667, 
2018.75, 2018.83333333333, 2018.91666666667, 2019, 2019.08333333333, 
2019.16666666667, 2019.25, 2019.33333333333, 2019.41666666667, 
2019.5), class = "yearmon"), neg = c(0, 0, 0, 0, 0, 0, 0, 0, 
-0.782066446199374, -1.33087717414387, -1.55401649141939, -1.9056578851487, 
-2.19869230289699, -1.99579537718088, -2.03857957860623, -2.14184701726747, 
-2.27461866979037, -2.39022691659445, -2.3732334198156, -1.83686080707261, 
-1.86553025598681), pos = c(0.550567625206492, 0.699954781241267, 
0.775518140437689, 0.647367030217637, 0.84562688020279, 0.923814518387379, 
0.686796306801202, 0.131849327496122, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0)), row.names = 960:980, class = "data.frame")

I want to plot the SPEI values with ggplot as I learned it here: How to format the x-axis of the hard coded plotting function of SPEI package in R?

library(ggplot2)
ggplot(test) + 
  geom_area(aes(x = Period, y = pos), fill = "blue", col = "black") +
  geom_area(aes(x = Period, y = neg), fill = "red",  col = "black") +
  scale_y_continuous(limits = c(-2.25, 2.25), 
                     breaks = -2:2) +
  ylab("SPEI") + xlab("") +
  theme_bw() 

The result looks like this: SPEI

As you can see, when the sign changes from positive to negative, geom_area doesn't end/start at the same position. Anyone any idea how to fix this? I thought about using Date instead of yearmon, but got stuck with the same problem.


Solution

  • This is a gates and posts problem: Each geom_area at the inflection is starting and ending on a post, hence the overlap. They should be starting in the middle of the gate between the posts.

    This solution may be a bit heavy handed but I think it should apply where there are multiple changes from positive to negative and vice versa.

    
    
      library(ggplot2)
      library(tidyr)
      library(tibble)
      library(dplyr)
      library(lubridate)
      library(imputeTS)
    
    

    Determine when the data changes from positive to negative or vice versa

      inflections <- 
        test %>% 
        mutate(inflect = case_when(lag(neg) == 0 & pos == 0 ~ TRUE,
                                      lag(pos) == 0 & neg == 0 ~ TRUE,
                                      TRUE ~ FALSE),
               rowid = row_number() - 0.5) %>%
        filter(inflect) %>% 
        select(-inflect) %>% 
        mutate(Period = NA_Date_,
               pos = 0, 
               neg = 0)
    
    

    Insert a new row to mark the inflection point to allow inclusion of an intermediary time where both pos and neg can be zero.

    test1 <- 
      test %>% 
      rowid_to_column() %>%
      bind_rows(inflections) %>% 
      arrange(rowid)
    
    

    Impute a time when the data changes from pos to neg with a function from imputeTS.

    test1$Period <- na_interpolation(as.ts(test1$Period))
    
    

    plot

        ggplot(test1) + 
          geom_area(aes(x = Period, y = pos), fill = "blue", col = "black") +
          geom_area(aes(x = Period, y = neg), fill = "red",  col = "black") +
          scale_y_continuous(limits = c(-2.25, 2.25), 
                             breaks = -2:2) +
          ylab("SPEI") + xlab("") +
          theme_bw() 
    
    
    

    
    data
    
    ```
     test <-   structure(list(Period = structure(c(2017.83333333333, 2017.91666666667, 
        2018, 2018.08333333333, 2018.16666666667, 2018.25, 2018.33333333333, 
        2018.41666666667, 2018.5, 2018.58333333333, 2018.66666666667, 
        2018.75, 2018.83333333333, 2018.91666666667, 2019, 2019.08333333333, 
        2019.16666666667, 2019.25, 2019.33333333333, 2019.41666666667, 
        2019.5), class = "yearmon"), neg = c(0, 0, 0, 0, 0, 0, 0, 0, 
        -0.782066446199374, -1.33087717414387, -1.55401649141939, -1.9056578851487, 
        -2.19869230289699, -1.99579537718088, -2.03857957860623, -2.14184701726747, 
        -2.27461866979037, -2.39022691659445, -2.3732334198156, -1.83686080707261, 
        -1.86553025598681), pos = c(0.550567625206492, 0.699954781241267, 
        0.775518140437689, 0.647367030217637, 0.84562688020279, 0.923814518387379, 
        0.686796306801202, 0.131849327496122, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0)), row.names = 960:980, class = "data.frame")
    ```
    
    <sup>Created on 2020-05-22 by the [reprex package](https://reprex.tidyverse.org) (v0.3.0)</sup>