extract hours and seconds from POSIXct for plotting purposes in R

RDatetimeGgplot2Lubridate

R Problem Overview


Suppose I have the following data.frame foo

           start.time duration
1 2012-02-06 15:47:00      1
2 2012-02-06 15:02:00      2
3 2012-02-22 10:08:00      3
4 2012-02-22 09:32:00      4
5 2012-03-21 13:47:00      5

And class(foo$start.time) returns

[1] "POSIXct" "POSIXt" 

I'd like to create a plot of foo$duration v. foo$start.time. In my scenario, I'm only interested in the time of day rather than the actual day of the year. How does one go about extracting the time of day as hours:seconds from POSIXct class of vector?

R Solutions


Solution 1 - R

This is a good question, and highlights some of the difficulty in dealing with dates in R. The lubridate package is very handy, so below I present two approaches, one using base (as suggested by @RJ-) and the other using lubridate.

Recreate the (first two rows of) the dataframe in the original post:

foo <- data.frame(start.time = c("2012-02-06 15:47:00", 
                                 "2012-02-06 15:02:00",
                                 "2012-02-22 10:08:00"),
                  duration   = c(1,2,3))

Convert to POSIXct and POSIXt class (two ways to do this)

# using base::strptime
t.str <- strptime(foo$start.time, "%Y-%m-%d %H:%M:%S")

# using lubridate::ymd_hms
library(lubridate)
t.lub <- ymd_hms(foo$start.time)

Now, extract time as decimal hours

# using base::format
h.str <- as.numeric(format(t.str, "%H")) +
               as.numeric(format(t.str, "%M"))/60

# using lubridate::hour and lubridate::minute
h.lub <- hour(t.lub) + minute(t.lub)/60

Demonstrate that these approaches are equal:

identical(h.str, h.lub)

Then choose one of above approaches to assign decimal hour to foo$hr:

foo$hr <- h.str

# If you prefer, the choice can be made at random:
foo$hr <- if(runif(1) > 0.5){ h.str } else { h.lub }

then plot using the ggplot2 package:

library(ggplot2)
qplot(foo$hr, foo$duration) + 
             scale_x_datetime(labels = "%S:00")

Solution 2 - R

You could rely on base R:

# Using R 2.14.2
# The same toy data
foo <- data.frame(start.time = c("2012-02-06 15:47:00", 
                                 "2012-02-06 15:02:00",
                                 "2012-02-22 10:08:00"),
                  duration   = c(1,2,3))

Since class POSIXct contains date-time information in a structured manner, you can rely on substr to extract the characters in time positions within the POSIXct vector. That is, given you know the format of your POSIXct (how it would be presented when printed), you can extract hours and minutes:

# Extract hour and minute as a character vector, of the form "%H:%M"
substr(foo$start.time, 12, 16)

And then paste it to an arbitrary date to convert it back to POSIXct. In the example I use January first 2012, but if you don't specify a date and instead use format R uses the current date.

# Store time information as POSIXct, using an arbitrary date
foo$time <- as.POSIXct(paste("2012-01-01", substr(foo$start.time, 12, 16)))

And both plot and ggplot2 know how to format times in POSIXct out of the box.

# Plot it using base graphics
plot(duration~time, data=foo)

# Plot it using ggplot2 (0.9.2.1)
library(ggplot2)
qplot(x=time, y=duration, data=foo)

Solution 3 - R

Lubridate doesn't handle time of day data, so Hadley recommends the hms package for this type of data. Something like this would work:

library(lubridate)
foo <- data.frame(start.time = parse_datetime(c("2012-02-06 15:47:00", 
                                 "2012-02-06 15:02:00",
                                 "2012-02-22 10:08:00")),
                  duration   = c(1,2,3))


foo<-foo %>% mutate(time_of_day=hms::hms(second(start.time),minute(start.time),hour(start.time)))

Watch out for 2 potential issues - 1) lubridate has a different function called hms and 2) hms::hms takes the arguments in the opposite order to that suggested by its name (so that just seconds may be supplied)

Solution 4 - R

This code is much faster than converting to string and back to numeric

time <- c("1979-11-13T08:37:19-0500", "2014-05-13T08:37:19-0400");
time.posix <- as.POSIXct(time, format = "%Y-%m-%dT%H:%M:%S%z");
time.epoch <- as.vector(unclass(time.posix));
time.poslt <- as.POSIXlt(time.posix, tz = "America/New_York");
time.hour.new.york <- time.poslt$hour + time.poslt$min/60 + time.poslt$sec/3600;

> time;
[1] "1979-11-13T08:37:19-0500" "2014-05-13T08:37:19-0400"
> time.posix;
[1] "1979-11-13 15:37:19 IST" "2014-05-13 15:37:19 IDT"
> time.poslt;
[1] "1979-11-13 08:37:19 EST" "2014-05-13 08:37:19 EDT"
> time.epoch;
[1]  311348239 1399984639
> time.hour.new.york;
[1] 8.621944 8.621944

Solution 5 - R

It is ancient topic, but I have found very few questions and answers about this matter. My solution is the following

library(hms)
foo <- data.frame(start.time = c("2012-02-06 15:47:00", 
                             "2012-02-06 15:02:00",
                             "2012-02-22 10:08:00"),
              duration   = c(1,2,3))

foo$start.time = as.POSIXct( foo$start.time )

g1 = ggplot( ) + xlab("") + 
  geom_line( data = foo, aes(x = as.hms(start.time), y = duration ), color = "steelblue" )
g1

If you would like to add manual time (!) breaks, then

time_breaks =    as.POSIXlt(c(
                   "2012-02-06 12:35:00 MSK", 
                   "2012-02-06 13:15:00 MSK",
                   "2012-02-06 14:22:00 MSK",
                   "2012-02-06 15:22:00 MSK"))
 g1 + 
  scale_x_time( breaks = as.hms( time_breaks ) ) +
  theme(  axis.text.x = element_text( angle=45, vjust=0.25) ) 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionandrewjView Question on Stackoverflow
Solution 1 - RDavid LeBauerView Answer on Stackoverflow
Solution 2 - RchemmanView Answer on Stackoverflow
Solution 3 - RandyyyView Answer on Stackoverflow
Solution 4 - RLiran KatzirView Answer on Stackoverflow
Solution 5 - RStepan S. SushkoView Answer on Stackoverflow