2

I need some help comparing two times in R. Currently I have a column of imported data formatted as HH:%M %p (e.g. 03:15 PM, not 3:15 PM). I need to create a new column and flag any row wherein the time is before 10:00 AM.

I have scoured the internet and stared forever at similar solutions, but can't seem to get it to work.

Any help would be appreciated! Thank you!

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
stevenjoe
  • 329
  • 1
  • 4
  • 16
  • Are you having trouble comparing the times or having trouble flagging whether something is TRUE or FALSE? If the latter, you can use `ifelse` to do it within one line. – giraffehere Nov 04 '15 at 17:03

2 Answers2

4

UPDATE

This is a clear update that shows a neat way of using data.table::as.ITime. It pretty much combines dickoa 's answer, Frank's comment and my answer for a best result.

library(data.table)
#example vector
vec     <- c('09:00 PM', '11:00 PM', '09:00 AM')
#format can be used in the same way as in POSIXct
as.ITime(vec,  format = "%I:%M %p") < as.ITime('10:00')
[1] FALSE FALSE  TRUE

#notice that as.ITime does not create dates (alongside the times) as POSIX** does
#It only deals with times
#> as.ITime(vec,  format = "%I:%M %p")
#[1] "21:00:00" "23:00:00" "09:00:00"

Or by using a data.table as per Frank's comment below:

ivec <- as.ITime(vec)
setDT(list(ivec))[, V1 := as.ITime(vec, "%I:%M %p")]

Original Answer

#using safe.ifelse to keep attributes
#function borrowed from here:
#http://stackoverflow.com/questions/6668963/how-to-prevent-ifelse-from-turning-date-objects-into-numeric-objects
safe.ifelse <- function(cond, yes, no){ class.y <- class(yes)
                                        X <- ifelse(cond,yes,no)
                                        class(X) <-class.y; return(X)}


library(data.table)
#example vec
vec     <- c('09:00 PM', '11:00 PM', '09:00 AM')
#convert them to time using as.ITime from data.table and safe.ifelse
#basically I use safe.ifelse to detect PM in the vec
#if there is a PM then I convert that to an 24-hour time by adding 12 hours
new_vec <- safe.ifelse(grepl('PM', vec),  as.ITime(vec) + 12*3600, as.ITime(vec))

> new_vec
[1] "21:00:00" "23:00:00" "09:00:00"

Finally comparing with 10:00 AM

> new_vec < as.ITime('10:00')
[1] FALSE FALSE  TRUE

Or as mentioned by @Frank in the comments, since vec is a column in your data.frame this would probably be a better approach (and you wouldn't need to use safe.ifelse):

ivec <- as.ITime(vec)
setDT(list(ivec))[grepl('PM', vec), V1 := V1 + 12L*3600L ]

After seeing @dickoa 's answer, it seems that his answer with a format, works with as.ITime too:

vec     <- c('09:00 PM', '11:00 PM', '09:00 AM')
> as.ITime(vec, "%I:%M %p")
[1] "21:00:00" "23:00:00" "09:00:00"

Which means that a simple:

ivec <- as.ITime(vec)
setDT(list(ivec))[, V1 := as.ITime(vec, "%I:%M %p")]

Would be enough. So, IMO a combination of our answers (and @Frank 's comment) would probably be the best solution (as.ITime will not create the dates that strptime does).

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • 1
    @Frank Good one! Didn't really know how to compose the answer, missing a reproducible example but since he mentions he has a column of such times, yours is probably the best way. I ll add to my answer. Thanks! – LyzandeR Nov 04 '15 at 17:30
  • 1
    Great answer. I didn't know about `ITime` before thanks. – dickoa Nov 04 '15 at 19:42
3

You can strptime to convert your times to POSIXlt and then compare it to your target time.

time <- c("11:20 AM", "03:15 AM", "09:20 PM", "12:23 AM", "10:01 AM")
target <- "10:00 AM"

strptime(time, "%I:%M %p") > strptime(target, "%I:%M %p")
## [1]  TRUE FALSE  TRUE FALSE  TRUE
dickoa
  • 18,217
  • 3
  • 36
  • 50
  • This is a good answer (voted up). I used part of your answer with mine (giving you credit of course) as I believe a combination of the two is the best answer. If you feel offended by it I will remove the edit. – LyzandeR Nov 04 '15 at 17:46
  • @dickoa, PERFECT! THANK YOU! Thanks everyone for your help! – stevenjoe Nov 04 '15 at 18:17