rdatetimeggplot2

How can I reorder and filter a range of time-formatted data in ggplot?


I have data from recordings made each evening from 21:00:00 to 5:59:59. I'm trying to make a bar plot of the compiled data, with time on the x-axis. However, I'm having trouble displaying only the times I have data for (i.e., omitting the times between 06:00:00 and 20:59:59). Furthermore, I want to represent the data as taking place over the course of the night, so starting at 21:00:00 and ending at 06:00:00.

My data look like this:

> head(songs)
  Selection Begin.Time..s. End.Time..s.                       file       date
1         1      0.0959375    0.4159375  Baldy-20240823_030000.WAV 2024-08-23
2         1      0.4799375    1.1199375  Baldy-20240825_030000.WAV 2024-08-25
3         2      1.1839375    1.5999375  Baldy-20240823_030000.WAV 2024-08-23
4         1      1.7599375    2.3359375 Quarry-20240905_030000.WAV 2024-09-05
5         3      2.1439375    2.8479375  Baldy-20240823_030000.WAV 2024-08-23
6         2      2.7199375    3.3919375  Baldy-20240825_030000.WAV 2024-08-25
  Occupancy review overlap   songs clock                time
1       0.5      t               t 21:00 2025-04-17 21:00:00
2      0.45      t               t 21:00 2025-04-17 21:00:00
3    0.7692      t               t 21:00 2025-04-17 21:00:00
4       0.5        overlap overlap 21:00 2025-04-17 21:00:00
5    0.6818        overlap overlap 21:00 2025-04-17 21:00:00
6    0.7619        overlap overlap 21:00 2025-04-17 21:00:00
> tail(songs)
       Selection Begin.Time..s. End.Time..s.                       file
205490       432       32394.59     32395.01 Quarry-20240907_030000.WAV
205491       671       32395.23     32395.78  Baldy-20240902_030000.WAV
205492       672       32396.42     32396.96  Baldy-20240902_030000.WAV
205493       673       32397.70     32398.27  Baldy-20240902_030000.WAV
205494       433       32397.73     32398.11 Quarry-20240907_030000.WAV
205495       674       32399.17     32399.62  Baldy-20240902_030000.WAV
             date Occupancy review overlap songs clock                time
205490 2024-09-07    0.5385      t             t 05:59 2025-04-17 05:59:00
205491 2024-09-02    0.8235      t             t 05:59 2025-04-17 05:59:00
205492 2024-09-02    0.8824      t             t 05:59 2025-04-17 05:59:00
205493 2024-09-02    0.7778      t             t 05:59 2025-04-17 05:59:00
205494 2024-09-07    0.5833      t             t 05:59 2025-04-17 05:59:00
205495 2024-09-02    0.9286      t             t 05:59 2025-04-17 05:59:00

Using this code...

library(ggplot2)

ostf1 = ggplot(songs,aes(x=time,fill=songs)) + geom_bar() + theme_classic() +
  labs(x="Time", y="Count") + 
  theme(legend.title = element_blank()) + 
  scale_fill_hue(labels = c("Overlapping", "Single")) +  
  scale_y_continuous(breaks=seq(0,10000,by=100), expand = c(0,0)) +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  scale_x_datetime(date_breaks = "1 hours", date_labels = "%H:%M", expand = c(0,0))

...I get the figure below, with the times ordered from 00:00:00 to 23:59:59, and a big gap in the middle:

How can I reorder the two blocks of data, so that the x-axis reads from 21:00:00 to 06:00:00, and get rid of the big empty space?

EDIT: I neglected to mention that, while the data are all labeled as being on the same date, these recordings actually took place each night over several months. The dates in the data are wrong - they're just the date I converted the times to dttm. Hopefully that explains why I'm trying to eliminate the daytime hours and reorder the arrangement in the figure. Sorry for not explaining that in my original post!


Solution

  • You can shift the times that are between 9 PM and midnight a day back, so they appear before and adjacent to the rest of your data with no gap.

    Note that in the example below, I don't have all of your data, so there are some gaps but Time axis spans from 21 to 6.

    # sample data
    songs <- data.frame(
      Selection = c(1, 1, 2, 1, 3, 2, 432, 671, 672, 673, 433, 674),
      Begin.Time..s. = c(0.0959375, 0.4799375, 1.1839375, 1.7599375, 2.1439375, 
                         2.7199375, 32394.59, 32395.23, 32396.42, 32397.70, 32397.73, 32399.17),
      End.Time..s. = c(0.4159375, 1.1199375, 1.5999375, 2.3359375, 2.8479375, 
                       3.3919375, 32395.01, 32395.78, 32396.96, 32398.27, 32398.11, 32399.62),
      file = c("Baldy-20240823_030000.WAV", "Baldy-20240825_030000.WAV", "Baldy-20240823_030000.WAV", 
               "Quarry-20240905_030000.WAV", "Baldy-20240823_030000.WAV", "Baldy-20240825_030000.WAV", 
               "Quarry-20240907_030000.WAV", "Baldy-20240902_030000.WAV", "Baldy-20240902_030000.WAV", 
               "Baldy-20240902_030000.WAV", "Quarry-20240907_030000.WAV", "Baldy-20240902_030000.WAV"),
      date = as.Date(c("2024-08-23", "2024-08-25", "2024-08-23", "2024-09-05", "2024-08-23", "2024-08-25", 
                       "2024-09-07", "2024-09-02", "2024-09-02", "2024-09-02", "2024-09-07", "2024-09-02")),
      Occupancy = c(0.5, 0.45, 0.7692, 0.5, 0.6818, 0.7619, 0.5385, 0.8235, 0.8824, 0.7778, 0.5833, 0.9286),
      review = c("t", "t", "t", "overlap", "overlap", "overlap", "t", "t", "t", "t", "t", "t"),
      songs = c("t", "t", "t", "overlap", "overlap", "overlap", "t", "t", "t", "t", "t", "t"),
      clock = c("21:00", "21:00", "21:00", "21:00", "21:00", "21:00", "05:59", "05:59", "05:59", "05:59", "05:59", "05:59"),
      time = as.POSIXct(c("2025-04-17 21:00:00", "2025-04-17 21:10:00", "2025-04-17 21:10:00", "2025-04-17 21:20:00", 
                          "2025-04-17 21:00:00", "2025-04-17 21:10:00", "2025-04-17 05:59:00", "2025-04-17 05:59:00", 
                          "2025-04-17 05:39:00", "2025-04-17 05:59:00", "2025-04-17 05:49:00", "2025-04-17 05:39:00"))
    )
    
    library(ggplot2)
    library(dplyr)
    
    songs %>% 
      ## shifting 21-24 a day back
      mutate(time2 = if_else(lubridate::hour(time) < 21, 
                             time, time - 24 * 60 * 60)) %>% 
    ggplot(aes(x = time2, fill = songs)) + 
      geom_bar() + 
      scale_x_datetime(name = "Time",
                       date_breaks = "1 hours", date_labels = "%H:%M",
                       expand = c(0,0)) +
      scale_y_continuous(name = "Count",
                         ## commenting out for the sample data
                         #breaks = seq(0, 10000, by=100), 
                         expand = c(0, 0)) +
      scale_fill_hue(labels = c("Overlapping", "Single")) +  
      theme_classic() +
      theme(legend.title = element_blank(),
            axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))
    

    Another method is to use ggbreak to keep the right order, but remove the empty space.

    songs %>% 
      ggplot(aes(x = time, fill = songs)) + 
      geom_bar() + 
      ggbreak::scale_x_break(breaks=as.POSIXct(c("2025-04-17 6:00:00", 
                                                 "2025-04-17 21:00:00")), 
                             scales = 0.5) +
      scale_x_continuous(breaks = as.numeric(as.POSIXct("2025-04-17 0:00:00") + 
                                               c(0:6, 21:24) * 3600),
                         limits = as.numeric(as.POSIXct("2025-04-17 0:00:00") + 
                                               c(0, 24) * 3600),
                         labels = \(dt) {format(as.POSIXct(dt, 
                                                           origin = "1970-01-01"), 
                                                "%H:%M")}) +
      scale_y_continuous(## commenting out for the sample data
                         #breaks = seq(0, 10000, by=100),
                         expand = c(0,0)) +
      labs(x = "Time", y = "Count") +
      scale_fill_hue(labels = c("Overlapping", "Single")) +  
      theme_classic() +
      theme(legend.title = element_blank(),
            axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
            axis.text.x.top = element_blank(),
            axis.ticks.x.top = element_blank(),
            axis.line.x.top = element_blank())