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