'R ggplot date time major and minor axis breaks not aligning

I'm having a problem that I am surprised I haven't encountered before and that I can't see any answers about. I am plotting some date time data in ggplot and want to force daily major breaks and quarter daily minor breaks (0, 6, 12, 18). When I set date_breaks = "1 day", date_minor_breaks = "6 hours" the date breaks are at middnight but the minor breaks depend on the start time of the data so I have weird offset minor breaks.

I will be making similar plots for a whole range of data which will have variable start times and end times (read from csvs or filtered from datasets based on certain conditions) so I am looking for a generic solution which will work without a fixed start time.

I have made the minor gridlines blue and major gridlines black in the example just to be more clear

library(lubridate)
library(ggplot2)

testdat <- data.frame(DateTime = seq.POSIXt(from = ymd_hm("20210101 1200"), length.out = 130, by = "1 hour", tz = "UTC"),
                      y = rnorm(130))


ggplot(testdat, aes(x = DateTime, y = y))+geom_point()+
   scale_x_datetime(date_breaks = "1 day", date_minor_breaks = "6 hours", timezone  = "UTC",
                     date_labels = "%d %b %H:%M")+theme_bw()+
  theme(panel.grid.major.x =  element_line(colour = "black"),
        panel.grid.minor.x =  element_line(colour = "blue"))

enter image description here



Solution 1:[1]

One approach would be to remove the automatic scale padding with expand = c(0,0) and to specify limits that are an integer # of minor breaks on either side:

ggplot(testdat, aes(x = DateTime, y = y))+geom_point()+
  scale_x_datetime(date_breaks = "1 day", date_minor_breaks = "6 hours", timezone  = "UTC",
                   date_labels = "%d %b %H:%M", expand = c(0,0), 
                   limits = c(min(testdat$DateTime) - hours(6), 
                              max(testdat$DateTime) + hours(6)))+
  theme_bw()+
  theme(panel.grid.major.x =  element_line(colour = "black"),
        panel.grid.minor.x =  element_line(colour = "blue"))

enter image description here

Solution 2:[2]

Using the same units in the date_breaks and date_minor_breaks statements will align the axis breaks, but doesn't guarantee they start at a sensical date/time.

ggplot(testdat, aes(x = DateTime, y = y)) + 
    geom_point() +
    scale_x_datetime(date_breaks = "1 day", 
                     minor_breaks = function(x) {
                         seq.POSIXt(lubridate::floor_date(x[1], "day"),
                                    lubridate::ceiling_date(x[2], "day"),
                                    by = "6 hours") 
                         },
                     date_labels = "%d %b %H:%M") +
    theme_bw() +
    theme(panel.grid.major.x =  element_line(colour = "black"),
          panel.grid.minor.x =  element_line(colour = "blue"))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jon Spring
Solution 2