首页 > 解决方案 > plot arrival and departure times that cross midnight

问题描述

I am trying to plot an animal's arrival to and departure from a given site. The animal arrives in the afternoon/evening and departs the following morning. Sometimes the animal arrives after midnight, thus both arrival and departure occur on the same date.

How can I scale the x axis so that the times are centered around midnight? In both examples in the reprex below, the problem is that the x axis is scaled from morning to night, thus showing the animal's departure time from the previous day before its arrival time that night, whereas what I would like is to show each day's arrival time and what time it departed the next day. How can I scale the x axis so each segment begins at the arrival time and ends at the departure time the following morning?

Thank you for any ideas!

library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union
library(hms)
#> 
#> Attaching package: 'hms'
#> The following object is masked from 'package:lubridate':
#> 
#>     hms
library(tidyverse)
library(ggplot2)
library(reprex)

df <- data.frame(
  date = as.Date(c("2021-04-19", "2021-04-20", "2021-04-21", "2021-04-22", "2021-04-23", "2021-04-24", "2021-04-26")),
  arrival_time = ymd_hms(c("2021-04-19 19:03:00", "2021-04-20 19:50:00", "2021-04-21 20:04:00", "2021-04-22 20:52:00", "2021-04-23 21:06:00",
                           "2021-04-24 21:22:00", "2021-04-26 01:47:00")),
  departure_time = ymd_hms(c("2021-04-20 06:00:00", "2021-04-21 05:47:00", "2021-04-22 06:23:00", "2021-04-23 05:56:00",
                            "2021-04-24 04:59:00", "2021-04-25 06:32:00", "2021-04-27 06:40:00"))
)

# attempt 1 - plot separate date variable on y axis
ggplot(df) +
  geom_segment(aes(x = as_hms(arrival_time), xend = as_hms(departure_time), y = date, yend = date)) +
  scale_y_date(date_breaks = "1 day", date_labels = "%d %b") +
  labs(x = "Time at Site",
       y = "Date")

# attempt 2 - plot actual date of POSIXct variable on y axis 
ggplot(df) +
  geom_segment(aes(x = as_hms(arrival_time), xend = as_hms(departure_time), y = date(arrival_time), yend = date(departure_time))) +
  scale_y_date(date_breaks = "1 day", date_labels = "%d %b") +
  labs(x = "Time at Site",
       y = "Date")

Created on 2021-07-29 by the reprex package (v1.0.0)

标签: rggplot2time-serieslubridate

解决方案


You could try subtracting 12 hours from the times then adding 12 hours to their labels.

First write a labelling function:

# The hms format stores time as seconds internally.
# There are 86400 seconds in 24 hours and 43200 seconds in 12 hours, so our
# labelling function adds 43200 seconds to increase the time values by 12
# hours, then gives the result modulo 86400 to deal with values above 24 hours.
# The result is an integer number of seconds, so we need to convert this with as_hms.
# Finally we take the first 5 characters of the result with substr to give %H:%M
# formatted character strings as the labels

labelling_fn <- function(x) {
  (as.numeric(x + 43200) %% 86400) %>%
     as_hms()                      %>%
     substr(1, 5)
}

Now pass the labelling function to the labels argument of scale_x_time

df %>%
ggplot(aes(y = date(arrival_time), xmin = as_hms(arrival_time - hours(12)),
           xmax = as_hms(departure_time - hours(12)))) +
  geom_linerange() +
  scale_x_time(labels = labelling_fn) +
  scale_y_date(date_breaks = "1 day", date_labels = "%d %b") +
  labs(x = "Time at Site",
       y = "Date")

enter image description here


推荐阅读