Home > Net >  How to order time in y axis
How to order time in y axis

Time:04-28

I have a data frame (a tibble) like this:

library(tidyverse)
library(lubridate)
x = tibble(date=c("2022-04-25 07:04:07", "2022-04-25 07:09:07", "2022-04-25 07:14:07", "2022-04-26 07:04:07"),
              value=c("on", "off", "on", "off"))
x$day<- as.factor(day(x$date))
x$time <- paste0(str_pad(hour(x$date),2,pad="0"),":",str_pad(minute(x$date),2,pad="0"))

When I plot the data:

x %>% ggplot()   geom_col(aes(x=day,y=time, fill=value))

enter image description here

the times in the y axis do not follow the bars. Each time data is supposed to be side by side with each bar segment. I tried using as.factor(time) but that didn't solve. I also tried to add a numeric scale:

x = tibble(date=c("2022-04-25 07:04:07", "2022-04-25 07:09:07", "2022-04-25 07:14:07", "2022-04-26 07:04:07"),
fake_y=c(1,1,1,1)
value=c("on", "off", "on", "off"))
x %>% ggplot()   geom_col(aes(x=day,y=fake_y, fill=value))

but then the order of the on/off bars is lost. enter image description here How can I fix this?

CodePudding user response:

Since you are looking for a time line, you would probably be best with geom_segment rather than geom_col. The reason is that since you might have multiple 'on' or 'off' values in a single day, it would be difficult to get these to stack correctly. You would also need to diff the on-off times to get them to stack. Furthermore, your labels would be wrong using columns if "off" represents the time of going from an on state to an off state.

When working with times in R, it is often best to keep them in time format for plotting. If you convert times to character strings before plotting, they will be interpreted as factor levels, and therefore will not be proportionately spaced correctly.

Since you want to have the day along one axis, you will need quite a bit of data manipulation to ensure that you record the state at the start of each day and the end of each day, but it can be achieved by doing:

p <- x %>% 
  mutate(date = as.POSIXct(date)) %>%
  mutate(day = as.factor(day(date))) %>%
  group_by(day) %>%
  group_modify(~ add_row(.x,
          date = floor_date(as.POSIXct(first(.x$date)), 'day'), 
          value = ifelse(first(.x$value) == 'on', 'off', 'on'),
          .before = 1)) %>%
  group_modify(~ add_row(.x,
          date = ceiling_date(as.POSIXct(last(.x$date)), 'day') - 1,
          value = last(.x$value))) %>%
  mutate(ends = lead(date)) %>%
  filter(!is.na(ends)) %>%
  mutate(date = hms::as_hms(date), ends = hms::as_hms(ends)) %>%
  ggplot(aes(x = day, y = date))  
  geom_segment(aes(xend = day, yend = ends, color = value),
               size = 20)  
  coord_cartesian(ylim = c(25120, 26500))  
  labs(y = 'time')  
  guides(color = guide_legend(override.aes = list(size = 8)))

p

enter image description here

And of course, you can easily flip the co-ordinates if you wish, and apply theme elements to make the plot more appealing:

p   coord_flip(ylim = c(25120, 26500))  
  scale_color_manual(values = c('deepskyblue4', 'orange'))  
  theme_light(base_size = 16)

enter image description here

  • Related