Search code examples
ggplot2raster

how to create a raster plot displaying continuous durations of time for behavior (without ticks)


I need to create raster plot which displays time based data. Here is an example of the data I have. For each participant, I have durations of time (onsets and offsets) they spent in different locations (crib, floor, etc) across 24 hour day. I would like a type of raster that shows participant locations continuously, rather than as ticks. How do I go about writing that script in ggplot2?

id  onset       offset     location
1   06:00:00.0  06:35:00.0  hichair
1   06:35:00.0  08:00:00.0  carseat
1   08:00:00.0  09:00:00.0  chifurn
1   09:00:00.0  09:45:00.0  cribcradle
1   09:45:00.0  12:00:00.0  gndfl
1   12:00:00.0  12:45:00.0  chifurn
1   12:45:00.0  15:00:00.0  gndfl
1   15:00:00.0  16:00:00.0  chifurn
1   16:00:00.0  17:15:00.0  strowlkr
1   17:15:00.0  18:00:00.0  carseat
1   18:00:00.0  18:30:00.0  hichair
1   18:30:00.0  19:30:00.0  gndfl
1   19:30:00.0  20:00:00.0  adlfurn
1   20:00:00.0  06:00:00.0  cribcradle
2   06:00:00.0  06:30:00.0  cribcradle
2   06:30:00.0  06:45:00.0  adlfurn
2   06:45:00.0  06:55:00.0  adlfurn
2   06:55:00.0  07:45:00.0  gndfl
2   07:45:00.0  07:55:00.0  arms
2   07:55:00.0  08:30:00.0  gndfl
2   08:30:00.0  08:50:00.0  hichair
2   08:50:00.0  09:45:00.0  strowlkr
2   09:45:00.0  10:00:00.0  cribcradle
2   10:00:00.0  11:00:00.0  cribcradle
2   11:00:00.0  11:05:00.0  cribcradle
2   11:05:00.0  11:15:00.0  hichair
2   11:15:00.0  11:35:00.0  infcarry
2   11:35:00.0  12:30:00.0  gndfl
2   12:30:00.0  13:00:00.0  hichair
2   13:00:00.0  13:30:00.0  gndfl
2   13:30:00.0  15:15:00.0  cribcradle
2   15:15:00.0  16:20:00.0  carseat
2   16:20:00.0  18:30:00.0  arms
2   18:30:00.0  18:45:00.0  infcarry
2   18:45:00.0  19:45:00.0  carseat
2   19:45:00.0  20:05:00.0  arms
2   20:05:00.0  06:00:00.0  cribcradle
3   06:00:00.0  07:00:00.0  adlfurn
3   07:00:00.0  07:05:00.0  adlfurn
3   07:05:00.0  07:45:00.0  arms
3   07:45:00.0  08:00:00.0  gndfl
3   08:00:00.0  08:15:00.0  hichair
3   08:15:00.0  08:30:00.0  adlfurn
3   08:30:00.0  08:45:00.0  strowlkr
3   08:45:00.0  09:00:00.0  arms
3   09:00:00.0  11:30:00.0  gndfl
3   11:30:00.0  12:30:00.0  hichair
3   12:30:00.0  15:00:00.0  cribcradle
3   15:00:00.0  15:25:00.0  hichair
3   15:25:00.0  17:00:00.0  gndfl
3   17:00:00.0  17:15:00.0  carseat
3   17:15:00.0  17:30:00.0  hichair
3   17:30:00.0  17:45:00.0  gndfl
3   17:45:00.0  18:05:00.0  carseat
3   18:05:00.0  18:25:00.0  arms
3   18:25:00.0  18:40:00.0  carseat
3   18:40:00.0  19:10:00.0  gndfl
3   19:10:00.0  19:25:00.0  hichair
3   19:25:00.0  19:45:00.0  carseat
3   19:45:00.0  19:50:00.0  arms
3   19:50:00.0  19:55:00.0  adlfurn
3   19:55:00.0  20:05:00.0  arms
3   20:05:00.0  20:10:00.0  adlfurn
3   20:10:00.0  20:15:00.0  arms
3   20:15:00.0  20:20:00.0  adlfurn
3   20:20:00.0  20:25:00.0  arms
3   20:25:00.0  03:30:00.0  cribcradle
3   03:30:00.0  04:00:00.0  arms
3   04:00:00.0  06:00:00.0  adlfurn

Here is an example from a published article of what I want (but in my case I'd have multiple colors depicting different locations)

Here is the script I currently have that provides a disjointed time line using onsets of locations. I also need to sort the time in the figure to display data from 6:00am current until 6:00am the following day. I'd like the bars to be continuous.

Thank you for your help!!

ggplot(loc_us) +
  geom_raster(aes(x=onset, y=id, fill = location)) +
  coord_equal() + theme_classic() +
  theme(legend.position="bottom") +
  scale_x_discrete(breaks=c("00:00:00.0", "06:00:00.0", "12:00:00.0", "20:00:00.0"))

photo of output my script is giving me


Solution

  • By converting onset and offset to datetime this can be achieved via geom_rect. The tricky part is the conversion to datetime and setting the correct dates. For the datetime conversion I first convert to periods using lubridate::hms and then to datetime via lubridate::as_datetime. Second, as the datetime conversion adds January 1, 1970 as the date we have to correct the dates in a second step, i.e. add one day to times between 00:00:00 and 06:00:00 or if offset time > onset time.

    library(ggplot2)
    library(dplyr)
    
    loc_us %>% 
      mutate(
        # convert to datetime
        onset = lubridate::as_datetime(lubridate::hms(onset)), 
        offset = lubridate::as_datetime(lubridate::hms(offset)),
        # set correct dates
        onset = case_when(
          onset < lubridate::as_datetime(lubridate::hms("06:00:00")) ~ onset  + lubridate::days(1),
          TRUE ~ onset),
        offset = case_when(
          offset < onset ~ offset + lubridate::days(1),
          TRUE ~ offset)
        ) %>% 
      ggplot() +
      geom_rect(aes(xmin=onset, xmax = offset, ymin=id-.4, ymax = id + .4, fill = location), color = "white") +
      scale_x_datetime(date_labels = "%H:%M") +
      theme_classic() +
      theme(legend.position="bottom")
    

    Created on 2020-05-25 by the reprex package (v0.3.0)