Search code examples
rggplot2dplyrlubridategridextra

How to prevent gridExtra::grid.arrage from automatically scaling the x axis in R?


I run each of the below plots within the loop with no problem, but for some reason, when I try to combine them in grid.arrange the axes get scaled automatically. I can't figure out how to not automatically scale. I literally just want to combine the ggplots together (as if I used par(mfrow = c(2,3)) with base R).

I think the fact that the X axis is really a re-formatted datetime (as.posixct) is throwing it off. Ideally, I would like the X axis to display just the hour.

library(ggplot2)
library(bp)

df <- data(hypnos_data)

ids <- unique(df$ID)
id_tab <- cbind(ids, c(1:5))

index <- c(8:23, 0:7)

plot_list <- list()
for(i in ids){
  
  subs <- df[which(df$ID == i & df$VISIT == 1),]
  subs$DATE.TIME <- as.POSIXct(subs$DATE.TIME)
  subs$hour_rec <- lubridate::hour(subs$DATE.TIME)
  
  subs$hour_rec <- factor(subs$hour_rec, levels = as.character(index), ordered = T)
  
  row.names(subs) <- NULL
  
  tmp <- subs %>% dplyr::filter(WAKE == 0)
  min(tmp$hour_rec)
  
  p <- ggplot2::ggplot(subs, ggplot2::aes(x = DATE.TIME, y = SYST)) +
    
    geom_rect(aes(xmin = min(tmp$DATE.TIME), xmax = max(tmp$DATE.TIME), ymin = -Inf, ymax = Inf),
              fill = "navajowhite2", alpha = 0.03) + 
    
    ggplot2::geom_point(aes(y = SYST), col = 'blue') + 
    ggplot2::geom_smooth(aes(y = SYST), method = "loess", col = 'blue') + 
    ggplot2::geom_point(aes(y = DIAST), col = 'red') + 
    ggplot2::geom_smooth(aes(y = DIAST), method = "loess", col = 'red') + 

    scale_x_datetime(date_label = "%H:%M", date_breaks = "1 hour") +
    theme(axis.text.x = element_text(angle=45)) +
    
    ggtitle( paste("BP Profile for Subject: ", i, sep = "") )
  
  plot_list[[match(i, id_tab)]] <- p
  print(p)
  
  #grid::grid.newpage()
}

The above code should produce 5 plots similar to this (which are correct):

individual plot output

enter image description here

gridExtra::grid.arrange(grobs = plot_list, ncol = 2 )

However, trying to combine them using grid.arrange yields the following:

grid.arrange plot output

enter image description here


Solution

  • The issue is not in the grid.arrange It is due to the reference variable in ggplot2. Some of the axis data is reference values instead of stored values inside the plot. This result in the change in axis when the subs & tmp change for each for-loop interation. Here is a way to walk around it using eval(substitute(...))

    library(ggplot2)
    library(bp)
    library(doParallel)
    #> Loading required package: foreach
    #> Loading required package: iterators
    #> Loading required package: parallel
    
    data(hypnos_data)
    df <- hypnos_data
    ids <- unique(df$ID)
    id_tab <- cbind(ids, c(1:5))
    
    index <- c(8:23, 0:7)
    
    plot_list <- list()
    plot_list <- foreach(i = ids) %do% {
      eval(substitute({
        subs <- df[which(df$ID == i & df$VISIT == 1),]
        subs$DATE.TIME <- as.POSIXct(subs$DATE.TIME)
        subs$hour_rec <- lubridate::hour(subs$DATE.TIME)
        
        subs$hour_rec <- factor(subs$hour_rec, levels = as.character(index), ordered = T)
        
        row.names(subs) <- NULL
        
        tmp <- subs %>% dplyr::filter(WAKE == 0)
        min(tmp$hour_rec)
        
        p <- ggplot2::ggplot(subs, ggplot2::aes(x = DATE.TIME, y = SYST)) +
          
          geom_rect(aes(xmin = min(tmp$DATE.TIME), xmax = max(tmp$DATE.TIME), ymin = -Inf, ymax = Inf),
            fill = "navajowhite2", alpha = 0.03) + 
          ggplot2::geom_point(aes(y = SYST), col = 'blue') + 
          ggplot2::geom_smooth(aes(y = SYST), method = "loess", col = 'blue') + 
          ggplot2::geom_point(aes(y = DIAST), col = 'red') + 
          ggplot2::geom_smooth(aes(y = DIAST), method = "loess", col = 'red') + 
          
          scale_x_datetime(date_label = "%H:%M", date_breaks = "1 hour") +
          theme(axis.text.x = element_text(angle=45)) +
          
          ggtitle( paste("BP Profile for Subject: ", i, sep = "") )
      }, list(subs = as.name(paste0("subs_", i)), tmp = as.name(paste0("tmp_", i)))))
      
      p
    }
    
    gridExtra::grid.arrange(grobs = plot_list)
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    

    Output plot

    Created on 2021-03-31 by the reprex package (v1.0.0)