5

I am creating a timetable plot to show how many events are in each time block. I have calculated all this information from lubridate and can plot it on ggplot fine. But I need to reverse/flip the axis so that the top shows 8am and goes down to 5pm rather than 5pm to 8am.

This is what a snippet of my end result currently looks like. Just need to reverse the time order and it will be perfect. This extends from 8am-5pm and M-F.

enter image description here

I tried using scale_y_reverse but this does not work.

I saw this post but I haven't been able to figure out how to use it for my situation: Reverse datetime (POSIXct data) axis in ggplot

Sample Data (created with dput fn):

df <- structure(list(ID = c(4108L, 4165L, 1504L, 2570L, 1523L, 2556L, 
3224L, 1503L, 3220L, 837L), START_TIME = structure(c(1504267200, 
1504281600, 1504258200, 1504278000, 1504263600, 1504256400, 1504258200, 
1504274400, 1504258200, 1504256400), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), END_TIME = structure(c(1504270799, 1504285199, 
1504263599, 1504281599, 1504268999, 1504259999, 1504263599, 1504279799, 
1504263599, 1504259999), class = c("POSIXct", "POSIXt"), tzone = "UTC"), 
    Day = structure(c(5L, 5L, 2L, 3L, 2L, 3L, 4L, 2L, 4L, 1L), .Label = c("M", 
    "T", "W", "R", "F"), class = "factor")), row.names = c(322L, 
351L, 112L, 188L, 125L, 179L, 298L, 111L, 294L, 8L), class = "data.frame")

My Code:

library(ggplot2)
library(scales)

ggplot(df) +
    geom_rect(aes(xmin= 0, xmax= 2, ymin = START_TIME, ymax = END_TIME), 
              color = "#ffffff", 
              position="dodge") + 
    scale_y_datetime(date_breaks="1 hour", labels = date_format("%I:%M %p"), expand = expand_scale(.1)) + 
    theme_minimal() +
    theme(axis.ticks.x = element_blank(),
            axis.text.x = element_blank(),
            panel.grid = element_blank(),
            panel.grid.major.y = element_line(color = "#cccccc"),
            axis.title = element_blank()) +
    facet_wrap(~Day, nrow=1) 
markus
  • 25,843
  • 5
  • 39
  • 58
Sahir Moosvi
  • 549
  • 2
  • 21

2 Answers2

5

I haven't been able to do this within the datetime scale, but one alternative is to convert to a numeric scale and handle the labeling manually:

library(lubridate)
df %>% 
  mutate(stime = hour(START_TIME) + minute(START_TIME)/60,
         etime = hour(END_TIME) + minute(END_TIME)/60) %>%

  ggplot() +
  geom_rect(aes(xmin= 0, xmax= 2, ymin = stime, ymax = etime), 
            color = "#ffffff", 
            position="dodge") + 
  scale_y_reverse(breaks = 6:23, 
                     labels = c(paste0(6:11, ":00 AM"), 
                                "12:00 PM",
                                paste0(1:11, ":00 PM")), 
                     expand = expand_scale(.1)) + 
  theme_minimal() +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        panel.grid = element_blank(),
        panel.grid.major.y = element_line(color = "#cccccc"),
        axis.title = element_blank()) +
  facet_wrap(~Day, nrow=1) 

enter image description here

Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • This helped to get something working in a pinch so thank you! I am hoping someone has a solution using the datetime scale because this approach messes with aligning values from geom_text and other formatting. – Sahir Moosvi Sep 24 '18 at 23:26
1

I am not sure if this is what you are looking for. Using the method from the question you referenced, I predefined the breaks for the y-axis and then created the reverse date transformer (from the referenced question).

library(ggplot2)
library(scales)

#create reverse time transformer function
#https://stackoverflow.com/questions/43625341/reverse-datetime-posixct-data-axis-in-ggplot#43626186
c_trans <- function(a, b, breaks = b$breaks, format = b$format) {
  a <- as.trans(a)
  b <- as.trans(b)
  name <- paste(a$name, b$name, sep = "-")

  trans <- function(x) a$trans(b$trans(x))
  inv <- function(x) b$inverse(a$inverse(x))
  trans_new(name, trans, inverse = inv, breaks = breaks, format=format)
}
rev_date <- c_trans("reverse", "time")

#create breaks in 1 hour increment
#mybreaks<-seq(as.POSIXct("2017-09-01 9:00", tz="UTC"), as.POSIXct("2017-09-01 18:00:00", tz="UTC"), 3600)
mybreaks=seq(as.POSIXct("2017-09-01 4:00", tz="UTC"), length.out = 20, by=3600)


#use scale continuous 
# to use the default breaks remove breaks=mybreaks
ggplot(df) +
  geom_rect(aes(xmin= 0, xmax= 2, ymin = START_TIME, ymax = END_TIME), 
            color = "#ffffff", 
            position="dodge") + 
  scale_y_continuous(breaks=mybreaks, labels = date_format("%I:%M %p"), expand = expand_scale(.1), trans = rev_date) + 
  theme_minimal() +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        panel.grid = element_blank(),
        panel.grid.major.y = element_line(color = "#cccccc"),
        axis.title = element_blank()) +
  facet_wrap(~Day, nrow=1) 

enter image description here

Dave2e
  • 22,192
  • 18
  • 42
  • 50
  • This is useful too, it saves me from creating new columns but it seems to be affected but still requires manually specifying. Perhaps we cannot do this nicely yet. I might file a feature a request for it. Thanks! – Sahir Moosvi Sep 25 '18 at 19:02