Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

round a date in R to an arbitrary minute/hour level of precision

I'm looking to group dates in R by an arbitrary level of precision.

It's pretty straightforward to do this to the nearest hour or minute, using e.g. lubridate:

library(lubridate)
nearest_hour = floor_date(now(), 'hour')

You can then group a list of such dates with e.g. a simple summarise ddply from plyr.

What I'd like to do is round dates with arbitrary precision, e.g. to the nearest 15 minutes or every 3 hours:

nearest_three_hours = floor_date(now(), '3 hours')

There's a discussion of such things at http://r.789695.n4.nabble.com/Truncating-dates-and-other-date-time-manipulations-td866901.html but outside of cutting dates, there doesn't appear to have been any resolution.

like image 978
philadams Avatar asked May 29 '13 01:05

philadams


4 Answers

A little late, and I don't have rep to comment, but as Selva mentioned, lubridate now has this functionality:

library(lubridate)
round_date(now(), '3 hours')
floor_date(now(), '3 hours')
ceiling_date(now(), '3 hours')
like image 197
rjweyant Avatar answered Nov 14 '22 20:11

rjweyant


lubridate already floors to the nearest atomic unit. To get floor to the nearest 15 minutes, which I think is what you want to do (not round), you just need to map into the correct range via findInterval and a defined set of breakpoints. Try this floor_time, which is functionally equivalent to floor_date, but allows you to specify a variable # of units for seconds, minutes or hours.

floor_time <- function(x, k = 1, unit = c("second", "minute", "hour", "day", 
                                          "week", "month", "year")) {
  require(lubridate)

  nmax <- NULL

  switch(unit, second = {nmax <- 60},
         minute = {nmax <- 60},
         hour = {nmax <- 24})

  cuts <- seq(from = 0, to = nmax - 1, by = k)
  new <- switch(unit, 
                second = update(x, seconds = cuts[findInterval(second(x), cuts)]), 
                minute = update(x, minutes = cuts[findInterval(minute(x), cuts)], 
                                seconds = 0), 
                hour = update(x, hours = cuts[findInterval(hour(x), cuts)], 
                              minutes = 0, seconds = 0), 
                day = update(x, hours = 0, minutes = 0, seconds = 0), 
                week = update(x, wdays = 1, hours = 0, minutes = 0, seconds = 0), 
                month = update(x, mdays = 1, hours = 0, minutes = 0, seconds = 0), 
                year = update(x, ydays = 1, hours = 0, minutes = 0, seconds = 0))

  new
}
like image 43
giminey Avatar answered Nov 14 '22 18:11

giminey


You can try this, still based on the lubridate library

library(lubridate)
round_minute<-function(x,precision){
  m<-minute(x)+second(x)/60
  m.r<- round(m/precision)*precision
  minute(x)<-m.r
  second(x)<-0
  x
}

round_minute(ymd_hms(c("2013-06-03 22:53:00","2013-05-03 12:18:00","2013-05-03 00:10:00")),15)

> "2013-06-03 23:00:00 UTC" "2013-05-03 12:15:00 UTC" "2013-05-03 00:15:00 UTC"

The code handle well all the tricky situations thanks to lubridate. Of course this function works only for precision expressed in minutes but you can easily extend it to other units, and even create a generic function if you really need it.

like image 5
user2147028 Avatar answered Nov 14 '22 18:11

user2147028


lubridate now has a more generic round_date() function.

lubridate::round_date(date, "5 mins")
lubridate::round_date(date, "2 hours")
like image 5
Selva Avatar answered Nov 14 '22 19:11

Selva