rggplot2time-seriesaxisgeom-segment

How to deal with time series data to create new x and y axis lines to ggplot graph?


I'm trying to present a ggplot graph similar to Base R graphs. The issue I have is dealing with time series data (date and time) to draw the x and y axis properly.

I used the same approach provided by Baptiste to create an x and y axis function: R-style axes with ggplot

However, it doesn't look right (see image below). I am not sure how to fix the y-axis and I also don't know how to fix the x-axis so that the limits are only between 07:00:00AM and 10:00:00AM. Any help would be appreciated!

enter image description here

#y-axis
base_breaks_y <- function(x){
  b <- pretty(x)
  d <- data.frame(x=as.POSIXct(c("2019-01-30 07:00:00")), xend=as.POSIXct(c("2019-01-30 10:00:00")), y=min(b), yend=max(b))
  list(geom_segment(data=d, aes(x=x, y=y, xend=xend, yend=yend), inherit.aes=FALSE),
       scale_y_continuous(breaks=b))
}

#x-axis 
base_breaks_x <- function(x){
  b <- pretty(x)
  d <- data.frame(y=-Inf, yend=-Inf, x=min(b), xend=max(b))
  list(geom_segment(data=d, aes(x=x, y=y, xend=xend, yend=yend), inherit.aes=FALSE),
       scale_x_datetime(breaks=b, labels=date_format("%H:%M")))
}
#plot 
ggplot(data=df1, aes(x=date.time, y=value)) +
  geom_line() +
  #labels 
  ylab("Value") +
  xlab("Time Series") +
  #aesthetics
  theme_bw() +
  theme(panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        text = element_text(size=11)) +
  #scale date time range 
  base_breaks_x(df1$date.time) +
  base_breaks_y(df1$value)

Example data:

 > dput(df1)
structure(list(date.time = structure(c(1548833480, 1548833555, 
1548833630, 1548833705, 1548833781, 1548833856, 1548833931, 1548834006, 
1548834083, 1548834158, 1548834233, 1548834308, 1548834384, 1548834459, 
1548834534, 1548834609, 1548834685, 1548834760, 1548834835, 1548834910, 
1548834987, 1548835062, 1548835137, 1548835212, 1548835288, 1548835363, 
1548835438, 1548835513, 1548835590, 1548835665, 1548835740, 1548835815, 
1548835891, 1548835966, 1548836041, 1548836116, 1548836192, 1548836267, 
1548836342, 1548836417, 1548836494, 1548836569, 1548836644, 1548836719, 
1548836795, 1548836870, 1548836945, 1548837020, 1548837096, 1548837171, 
1548837246, 1548837321, 1548837398, 1548837473, 1548837548, 1548837623, 
1548837699, 1548837774, 1548837849, 1548837924, 1548838000, 1548838075, 
1548838150, 1548838225, 1548838302, 1548838377, 1548838452, 1548838527, 
1548838603, 1548838678, 1548838753, 1548838828, 1548838905, 1548838980, 
1548839055, 1548839130, 1548839206, 1548839281, 1548839356, 1548839431, 
1548839507, 1548839582, 1548839657, 1548839732, 1548839809, 1548839884, 
1548839959, 1548840034, 1548840110, 1548840185, 1548840260, 1548840335, 
1548840411, 1548840486, 1548840561, 1548840636, 1548840713, 1548840788, 
1548840863, 1548840938), class = c("POSIXct", "POSIXt"), tzone = "UTC"), 
    value = c(3139, 2261, 2147, 2184, 2469, 2356, 2231, 1894, 
    1679, 1710, 1634, 1642, 1611, 1484, 1415, 1404, 1367, 1343, 
    1261, 1252, 1190, 1135, 1072, 1014, 979, 943, 935, 947, 952, 
    943, 937, 938, 920, 887, 867, 910, 954, 1036, 1106, 1160, 
    1216, 1226, 1288, 1368, 1348, 1363, 1474, 1420, 1276, 1310, 
    1357, 1207, 1051, 951, 923, 963, 1002, 1044, 1118, 1178, 
    1217, 1246, 1201, 1305, 1352, 1367, 1379, 1375, 1433, 1537, 
    1625, 1663, 1756, 1831, 1802, 1858, 2003, 1915, 1874, 1847, 
    1850, 2028, 2220, 2396, 2689, 2063, 2857, 2021, 2140, 2282, 
    2397, 2613, 2590, 2662, 2615, 2693, 2802, 2805, 2945, 3151
    )), row.names = c(NA, 100L), class = "data.frame")

Solution

  • One option to fix your issue would be to use x = xend = -Inf for the y axis as in the original post but instead of setting it via the aesthetics, which will result in an error

    Error: Invalid input: time_trans works with objects of class POSIXct only

    set them as arguments:

    library(ggplot2)
    library(scales)
    
    base_breaks_y <- function(x) {
      b <- pretty(x)
      d <- data.frame(y = min(b), yend = max(b))
      list(
        geom_segment(
          data = d, aes(y = y, yend = yend),
          x = -Inf, xend = -Inf, inherit.aes = FALSE
        ),
        scale_y_continuous(breaks = b)
      )
    }
    
    ggplot(data = df1, aes(x = date.time, y = value)) +
      geom_line() +
      labs(y = "Value", x = "Time Series") +
      theme_bw() +
      theme(
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        text = element_text(size = 11)
      ) +
      base_breaks_x(df1$date.time) +
      base_breaks_y(df1$value)
    

    enter image description here

    EDIT Getting the limits for the x axis right requires slightly more work. First we have to set the limits. Second, we have to take account of the limits when computing the breaks and the range for the geom_segment:

    base_breaks_x <- function(x, limits = NULL) {
      x <- c(x, limits)
      b <- pretty(x)
      
      d <- data.frame(y = -Inf, yend = -Inf, x = min(b), xend = max(b))
      list(
        geom_segment(data = d, aes(x = x, y = y, xend = xend, yend = yend), inherit.aes = FALSE),
        scale_x_datetime(
          breaks = b, labels = date_format("%H:%M"),
          limits = limits
        )
      )
    }
    
    ggplot(data = df1, aes(x = date.time, y = value)) +
      geom_line() +
      labs(y = "Value", x = "Time Series") +
      theme_bw() +
      theme(
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        text = element_text(size = 11)
      ) +
      base_breaks_x(
        df1$date.time,
        limits = as.POSIXct(c("2019-01-30 07:00:00", "2019-01-30 10:00:00"), tz = "GMT")
      ) +
      base_breaks_y(df1$value)
    

    enter image description here