Search code examples
rggplot2boundsfacet-wraptimeserieschart

how to ggplot with upper and lower bound as shaded using facet_wrap in R?


I am trying to automate the process of plotting data using ggplot and the facet_wrap functionality. I want a single y-axis label instead individual plot Ob (i.e., A_Ob, B_ob etc) and also a single X-axis not all the plots having label for x-axis such as below. Below is my sample code using gridextra package. However, i would like to do it through facet_wrap as i have many other plots to draw which i think will save me sometime.

graphics.off()
rm(list = ls())

library(tidyverse)
library(gridExtra)

G1 = data.frame(A_Ob = runif(1000, 5, 50), A_Sim = runif(1000, 3,60), A_upper = runif(1000, 10,70), A_lower = runif(1000, 0, 45 ),
                B_Ob = runif(1000, 5, 50), B_Sim = runif(1000, 3,60), B_upper = runif(1000, 10,70), B_lower = runif(1000, 0, 45 ),
                C_Ob = runif(1000, 5, 50), C_Sim = runif(1000, 3,60), C_upper = runif(1000, 10,70), C_lower = runif(1000, 0, 45 ),
                D_Ob = runif(1000, 5, 50), D_Sim = runif(1000, 3,60), D_upper = runif(1000, 10,70), D_lower = runif(1000, 0, 45 ),
                Pos = 1:1000)

A1 = ggplot(data = G1, aes(x = Pos))+
  geom_line(aes(y = A_Ob), col = "black")+
  geom_line(aes(y = A_Sim), col = "blue")+
  geom_vline(xintercept = 750,  color = "red", size=1.5)+
  geom_ribbon(aes(ymin = A_upper, ymax = A_lower), fill = "grey70")

B1 = ggplot(data = G1, aes(x = Pos))+
  geom_line(aes(y = B_Ob), col = "black")+
  geom_line(aes(y = B_Sim), col = "blue")+
  geom_vline(xintercept = 750,  color = "red", size=1.5)+
  geom_ribbon(aes(ymin = B_upper, ymax = B_lower), fill = "grey70")

C1 = ggplot(data = G1, aes(x = Pos))+
  geom_line(aes(y = C_Ob), col = "black")+
  geom_line(aes(y = C_Sim), col = "blue")+
  geom_vline(xintercept = 750,  color = "red", size=1.5)+
  geom_ribbon(aes(ymin = C_upper, ymax = C_lower), fill = "grey70")

D1 = ggplot(data = G1, aes(x = Pos))+
  geom_line(aes(y = D_Ob), col = "black")+
  geom_line(aes(y = D_Sim), col = "blue")+
  geom_vline(xintercept = 750,  color = "red", size=1.5)+
  geom_ribbon(aes(ymin = D_upper, ymax = D_lower), fill = "grey70")

grid.arrange(A1,B1,C1,D1, nrow = 4)

Here is the result of the code enter image description here


Solution

  • You need to reshape your dataframe into a longer format and separate values for Ob, Sim, upper and lower.

    Using the function melt from data.table package can help you to achieve this:

    library(data.table)
    setDT(G1)
    Ob_cols = grep("_Ob",colnames(G1),value = TRUE)
    Sim_cols = grep("_Sim",colnames(G1),value = TRUE)
    Upper_cols = grep("_upper",colnames(G1), value = TRUE)
    Lower_cols = grep("_lower", colnames(G1), value = TRUE)
    g.m <- melt(G1, measure = list(Ob_cols,Sim_cols,Upper_cols,Lower_cols), value.name = c("OBS","SIM","UP","LOW"))
    
    levels(g.m$variable) <- c("A","B","C","D") 
    
       Pos variable       OBS       SIM       UP      LOW
    1:   1        A  5.965488 29.167666 26.66783 29.97259
    2:   2        A 23.855719  8.570245 43.75830 30.65616
    3:   3        A 16.947887 51.201047 15.20758 39.76122
    4:   4        A 49.883306  3.715319 34.38066 20.73177
    5:   5        A  5.021938  3.102880 30.05036 32.05123
    6:   6        A 19.887176 15.400853 53.67156 28.54982
    

    and now, you can plot it:

    library(ggplot2)
    ggplot(g.m, aes(x = Pos))+
      geom_line(aes(y = OBS), color = "black")+
      geom_line(aes(y = SIM), color = "blue")+
      geom_vline(xintercept = 750,color = "red", size = 1.5)+
      geom_ribbon(aes(ymin = UP, ymax = LOW), fill = "grey70")+
      facet_grid(variable~.)
    

    enter image description here

    EDIT: Adding annotations & renaming labels

    To rename and replace facet labels, you can re-define levels of variable and use facet_wrap instead of facet_grid using ncol = 1 as argument.

    To add multiple annotations on a single panel, you need to define a dataframe that you will use in geom_text.

    Altogether, you have to do:

    # renaming names of each facets:
    levels(g.m$variable) <- c("M1","M2","M3","M4")
    
    # Defining annotations to add:
    df_text <- data.frame(label = c("Calibration", "Validation"),
                           x = c(740,760),
                           y = c(65,65),
                           hjust = c(1,0),
                           variable = factor("M1", levels = c("M1","M2","M3","M4")))
    
    # Plotting
    ggplot(g.m, aes(x = Pos))+
      geom_line(aes(y = OBS), color = "black")+
      geom_line(aes(y = SIM), color = "blue")+
      geom_vline(xintercept = 750,color = "red", size = 1.5)+
      geom_ribbon(aes(ymin = UP, ymax = LOW), fill = "grey70")+
      facet_wrap(variable~., ncol = 1)+
      theme(strip.text.x = element_text(hjust = 0),
            strip.background = element_rect(fill = "white"))+
      geom_text(data = df_text, aes(x = x, y = y, label = label, hjust = hjust), color = "red")
    

    enter image description here

    Does it look what you are expecting ?