12

I want to plot occupancy rates for a particular parking garage on a particular day in a line chart making use of ggplot.

My dataframe looks as follows:

head(ParkingSub4)
    FreeSpaceShort ShortCapacity            DateTime OccupancyRateShort       Date  Weekday WeekNumber  Week
310801            257           373 2017-02-25 00:04:41          0.3109920 2017-02-25 Saturday         08 FALSE
310843            260           373 2017-02-25 00:09:41          0.3029491 2017-02-25 Saturday         08 FALSE
310885            261           373 2017-02-25 00:14:41          0.3002681 2017-02-25 Saturday         08 FALSE
310927            260           373 2017-02-25 00:19:41          0.3029491 2017-02-25 Saturday         08 FALSE
310969            260           373 2017-02-25 00:24:41          0.3029491 2017-02-25 Saturday         08 FALSE
311011            263           373 2017-02-25 00:29:41          0.2949062 2017-02-25 Saturday         08 FALSE

class(ParkingSub4$DateTime)
[1] "POSIXlt" "POSIXt" 

When I try to plot an overview of a particular day, let's say 23rd of February, 2017 I use the following code:

ggplot(data = ParkingSub4, 
       aes(x=DateTime, y=OccupancyRateShort)) + geom_line(size = 1.25) + facet_wrap(~Weekday) +
      scale_x_datetime(labels=date_format("%H:%m"), breaks = date_breaks("2 hours")) +
      theme_linedraw()

Then what I get is the following plot:

enter image description here

As you can see, the plot starts and ends at 23:02. I want the plot to start at 00:00:00 and end at 23:59:59 on that particular day. How can I do that?

Many thanks in advance!

EDIT/UPDATE: Adding the following does lead to the x-axis starting and ending with 00:00:

ggplot(data = ParkingSub4, 
   aes(x=DateTime, y=OccupancyRateShort)) + geom_line(size = 1.25) + facet_wrap(~Weekday) +
  scale_x_datetime(labels=date_format("%H:%m"), breaks = date_breaks("2 hours"), expand=c(0,0)) +
  xlim(c(as.POSIXct('2017-02-23 00:00:00', format = "%Y-%m-%d %H:%M:%S"),
     as.POSIXct('2017-02-24 00:00:00', format = "%Y-%m-%d %H:%M:%S"))) +
  theme_linedraw()

Only thing is I get the following message after executing: 'Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.'

This means that scale_x_datetime(labels=date_format("%H:%m"), breaks = date_breaks("2 hours") is overwritten by: xlim(c(as.POSIXct('2017-02-23 00:00:00', format = "%Y-%m-%d %H:%M:%S"), as.POSIXct('2017-02-24 00:00:00', format = "%Y-%m-%d %H:%M:%S")).

That's also not what I want, as now the breaks are set at 6 hours instead of 2 hours, and I can also not specify what information ("%H:%m") is set on the labels.

KoenV
  • 4,113
  • 2
  • 23
  • 38
Robert
  • 133
  • 1
  • 1
  • 7
  • #1 Someone posted already the solution: use the scales' `limits` argument instead of a separate `+ xlim`. #2 You prly want `%H:%M`. #3 You could use `library(lubridate); xlims <- c(floor_date(min(df$x), "day"), ceiling_date(max(df$x), "day"))` to calculate the limits automatically. #4 Check out the scales arguments `date_breaks` and `date_labels`. #5 In general, you should post a [minimal reproducible example](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example#answer-5963610) so that everyone is on the same page. – lukeA Mar 13 '17 at 13:13
  • The 'limits' argument doesn't work in this case, as also mentioned in the comment to the answer. It doesn't change the plot in any way.. – Robert Mar 13 '17 at 13:44
  • See my answer. So the 1st `f` and the 2nd one yield the same result for you? Apart from that, you still don't provide a reproducible example for the the thing that does not work 4u, so that we are all still in guess work mode and have to create some artificial data. – lukeA Mar 13 '17 at 13:49

4 Answers4

8

First things first, here is some reproducible data:

set.seed(1)
ParkingSub4 <- data.frame(DateTime = seq(as.POSIXlt('2017-02-22 23:00'), 
                                         as.POSIXlt('2017-02-24 01:00'), 
                                         len = 42), 
                          OccupancyRateShort = runif(42, 0, 1))
ParkingSub4$Weekday <- weekdays(ParkingSub4$DateTime)

Next, here is how to reproduce the problem with this data:

library(ggplot2)
library(scales)
ggplot(data = ParkingSub4[ParkingSub4$Weekday == "Thursday",], 
       aes(x = DateTime, y = OccupancyRateShort)) + 
       geom_line(size = 1.25) + 
       facet_wrap(~Weekday) +
       scale_x_datetime(labels = date_format("%H:%m"), 
                        breaks = date_breaks("2 hours")) +
       theme_linedraw()

Finally, here is a solution using the limits option to scale_x_datetime:

lims <- as.POSIXct(strptime(c("2017-02-23 00:00", "2017-02-24 00:00"), 
                   format = "%Y-%m-%d %H:%M"))
ggplot(data = ParkingSub4[ParkingSub4$Weekday == "Thursday",], 
       aes(x = DateTime, y = OccupancyRateShort)) + 
       geom_line(size = 1.25) + 
       facet_wrap(~Weekday) +
       scale_x_datetime(labels = date_format("%H:%m"), 
                        breaks = date_breaks("2 hours"), 
                        limits = lims) +
       theme_linedraw()

UPDATE: The following will remove the whitespace on the left and right of the graph and the breaks will be on the hour instead of at 2 minutes past:

lims <- as.POSIXct(strptime(c("2017-02-23 00:00", "2017-02-23 23:59"), 
                   format = "%Y-%m-%d %H:%M"))
ggplot(data = ParkingSub4, 
       aes(x = DateTime, y = OccupancyRateShort)) + 
       geom_line(size = 1.25) +            
       scale_x_datetime(labels = date_format("%H:%M"), 
                        breaks = date_breaks("2 hours"), 
                        limits = lims, 
                        expand = c(0, 0)) +
       theme_linedraw()
makeyourownmaker
  • 1,558
  • 2
  • 13
  • 33
  • 1
    Thanks for trying to help me out. Specifying 'lims' and adding 'limits = lims' actually still doesn't change the plot in any way, the x-axis still begins with 23:02 and the other values on the x-axis remain 1:02, 3:02, 5:02 etc.. What else could be wrong if this doesn't work? – Robert Mar 13 '17 at 12:40
  • 3
    Thanks! I used %m where I instead should have uses %M for displaying the minutes. Still with the limits specified it gave me a range from 23:00 till 23:00. Apparently it as a timezone issue. I now used `scale_x_datetime(labels=date_format("%H:%M", tz="CET")` and it gave me exactly what I was looking for :) – Robert Mar 13 '17 at 16:36
  • 1
    Glad you found a solution and good to know about the tz option to date_format. – makeyourownmaker Mar 13 '17 at 17:04
  • I was trying this **** forever, `limits=` just didn't work but this solved it: `scales::date_format("%H:%M", tz = "CET")`. It really belongs into the answer. – Humpelstielzchen Aug 27 '19 at 05:44
  • I ran all the code, and the final update still produces a plot where the start and end times are identical, instead of starting at 00:00 and ending at a specified time like @Robert was looking for. – Susie Derkins Dec 03 '20 at 23:49
  • @SusieDerkins Did you try setting the second lims value to the ending time specified by @Robert? That is "2017-02-23 23:59". Let me know if it works for you and I'll update my answer. If not try the other answers and the suggestions in the comments. Hope this helps you. – makeyourownmaker Dec 04 '20 at 09:37
2

Use expand parameter in your scale_x_datetime and set it to 0.

scale_x_datetime(labels=date_format("%H:%m"), breaks = date_breaks("2 hours"), expand=c(0,0))
drmariod
  • 11,106
  • 16
  • 64
  • 110
  • 1
    Yes, this works but there is a missing a closing bracket for the scale_x_datetime function which I cannot add due to stackoverflow's minimum edit of 6 characters! – makeyourownmaker Mar 13 '17 at 11:51
  • Thanks for the answer. For me this still doesn't do the trick. This removed the white space on the left and right from the graph, so now the line actually starts and ends where the graph starts and ends. However, the x-axis is still starting at 23:02 and ending at 23:02. How can I set it to actually start at 0:00 and have breaks at 2:00, 4:00, 6:00 all the way up to 0:00 on the next day? – Robert Mar 13 '17 at 12:25
1

In addition to my comment:

library(ggplot2)
library(lubridate)
df <- data.frame(x = seq(Sys.time(), Sys.time()+days(1), by = "2 mins"))
df$y = runif(nrow(df))

lims <- c(floor_date(min(df$x), "day"), ceiling_date(max(df$x), "day"))
f <- function(lims)
  ggplot(data = df, aes(x, y)) + 
    geom_line() +  
    scale_x_datetime(
      date_labels = "%H:%M", 
      date_breaks = "2 hours", 
      limits = lims,
      timezone = Sys.timezone(),
      expand = c(0,0)
    ) + 
    theme(
      plot.margin = margin(10,20,10,10), 
      axis.text.x = element_text(angle=90, vjust=.5)
    )

f(lims)
f(lims-c(days(1),0))
lukeA
  • 53,097
  • 5
  • 97
  • 100
1

I'm adding another answer to clarify the difference between using scale_x_datetime and coordinate_cartesian. Unlike scale_x_datetime, coordinate_cartesian will zoom the plot without removing any data that falls outside the plot limits.

For example. If we use the plot above:

library(ggplot2)
library(scales)

set.seed(1)
ParkingSub4 <- data.frame(DateTime = seq(as.POSIXlt('2017-02-22 23:00', tz = 'UTM'), 
                                         as.POSIXlt('2017-02-24 01:00', tz = 'UTM'), 
                                         len = 42), 
                          OccupancyRateShort = runif(42, 0, 1))
ParkingSub4$Weekday <- weekdays(ParkingSub4$DateTime)

lims <- as.POSIXct(strptime(c("2017-02-23 00:00", 
                              "2017-02-23 23:59"), 
                               format = "%Y-%m-%d %H:%M"), 
                   tz = 'UTM')
ggplot(data = ParkingSub4, 
       aes(x = DateTime, y = OccupancyRateShort)) + 
   geom_line(size = 1.25) +            
   scale_x_datetime(labels = date_format("%H:%M"), 
                    breaks = date_breaks("2 hours"), 
                    limits = lims, 
                    expand = c(0, 0)) +
   geom_smooth()+
   theme_linedraw()

Original plot (with a smoothing line to more easily see the difference between scale_x_datetime and coordinate_cartesian): enter image description here

If, for some reason, you wanted to focus on 7am to 7pm, you have 2 options. You can use scale_x_datetime (or xlim) to remove extra data:

lims2 <- as.POSIXct(strptime(c("2017-02-23 07:00", 
                               "2017-02-23 19:00"), 
                             format = "%Y-%m-%d %H:%M"), 
                    tz = 'UTM')

ggplot(data = ParkingSub4, 
       aes(x = DateTime, y = OccupancyRateShort)) + 
   geom_line(size = 1.25) +            
   scale_x_datetime(labels = date_format("%H:%M"), 
                    breaks = date_breaks("2 hours"), 
                    limits = lims2, 
                    expand = c(0, 0)) +
   geom_smooth()+
   theme_linedraw()

Which produces this plot: enter image description here

Alternatively, you can use coordinate_cartesian to zoom in on the plot without removing any of the data:

ggplot(data = ParkingSub4, 
       aes(x = DateTime, y = OccupancyRateShort)) + 
   geom_line(size = 1.25) +            
   scale_x_datetime(labels = date_format("%H:%M"), 
                    breaks = date_breaks("2 hours"), 
                    limits = lims, 
                    expand = c(0, 0)) +
   coord_cartesian(xlim = lims2) +
   geom_smooth()+
   theme_linedraw()

Note that the smooth is the same as in the original plot: enter image description here

filups21
  • 1,611
  • 1
  • 19
  • 22