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.
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')
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
}
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.
lubridate now has a more generic round_date() function.
lubridate::round_date(date, "5 mins")
lubridate::round_date(date, "2 hours")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With