Schillerlocke
Schillerlocke

Reputation: 343

ggplot change start point of x-axis for POSIXct variable of a single day

I would like to plot midpoints of a day on a graph. I want "22:00" to be the first mark on the x-axis. Then 00:00, 02:00 up until 10:00.

It should look somewhat like the plots in this paper: https://www.ncbi.nlm.nih.gov/core/lw/2.0/html/tileshop_pmc/tileshop_pmc_inline.html?title=Click on image to zoom&p=PMC3&id=5479630_pone.0178782.g002.jpg

How can I do that? Example data and my graph down below.

DF <- data.frame(time_start = c("20:00", "21:00", "22:00", "23:00", "24:00", "21:30", "22:30", "20:00"),
                 time_end = c("01:00", "05:00", "06:00", "07:00", "08:00", "5:30", "6:30", "04:00"))

library(dplyr)
library(lubridate)

DF <- DF %>%
  mutate_all(hm) %>%
  mutate(time_end = time_end + (time_end < time_start) * days(1),
         midpoint = seconds_to_period(as.numeric(time_start + time_end) / 2)) %>%
  mutate_all(~sprintf("%02d:%02d", hour(.), minute(.)))

DF$midpoint <- strptime(DF$midpoint, "%H:%M", tz = "UTC")
DF$midpoint <- as.POSIXct(DF$midpoint)

What I have tried with limits:

lims <- as.POSIXct(strptime(c("2020-06-24 22:00","2020-06-24 10:00"), format = "%Y-%m-%d %H:%M"))  
    
library(ggplot2)
    
ggplot(DF)+
geom_histogram(aes(x = midpoint, y = stat(width*density)), binwidth = 30*60)+
scale_x_datetime(date_breaks = "2 hour", date_labels = "%H:%M", limits = lims)

But it gives me only error messages.

Error in seq.int(0, to0 - from, by) : wrong sign in 'by' Additional: Error message :Removed 8 rows containing non-finite values (stat_bin).

Thanks in advance!

Upvotes: 1

Views: 948

Answers (1)

Maurits Evers
Maurits Evers

Reputation: 50718

Judging from the paper figures you show, I think the sample data you give is poorly chosen for the task at hand, as you will need a date along with the time in order to achieve what you're trying to do.

So provided I understood your question correctly, this is what I would do.

First, let's create sample data

t0 <- as.POSIXct("2020-06-24 22:00", format = "%Y-%m-%d %H:%M")
df <- data.frame(
    time_start = seq(t0, by = "hour", length.out = 10),
    time_end = seq(t0 + 3 * 3600, by = "hour", length.out = 10))

We can then calculate mid points from the time durations defined by time_start and time_end.

# Calculate midpoints
df <- transform(df, time_midpoint = as.POSIXct(
    (as.numeric(time_start) + as.numeric(time_end)) / 2,
    origin = "1970-01-01"))

Finally we plot, and format the time axis according to your requirements:

library(ggplot2)
ggplot(df, aes(time_midpoint, y = 10)) +
    geom_col() +
    scale_x_datetime(
        limits = c(min(df$time_start - 3600), max(df$time_end)),
        breaks = scales::date_breaks("2 hour"),
        date_labels = "%H:%M")

enter image description here

Or you can show the times as durations

ggplot(df, aes(t0, 1:10)) +
    geom_linerange(aes(xmin = time_start, xmax = time_end)) +
    scale_x_datetime(
        name = "Times",
        limits = c(min(df$time_start - 3600), max(df$time_end)),
        breaks = scales::date_breaks("2 hour"),
        date_labels = "%H:%M")

enter image description here

Upvotes: 1

Related Questions