R: Time to/from the weekend
In my last post I showed some examples using R’s lubridate package and another problem it made really easy to solve was working out how close a particular date time was to the weekend.
I wanted to write a function which would return the previous Sunday or upcoming Saturday depending on which was closer.
lubridate’s floor_date and ceiling_date functions make this quite simple.
e.g. if we want to round the 18th December down to the beginning of the week and up to the beginning of the next week we could do the following:
> library(lubridate)
> floor_date(ymd("2014-12-18"), "week")
[1] "2014-12-14 UTC"
> ceiling_date(ymd("2014-12-18"), "week")
[1] "2014-12-21 UTC"
For the date in the future we actually want to grab the Saturday rather than the Sunday so we’ll subtract one day from that:
> ceiling_date(ymd("2014-12-18"), "week") - days(1)
[1] "2014-12-20 UTC"
Now let’s put that together into a function which finds the closest weekend for a given date:
findClosestWeekendDay = function(dateToLookup) {
before = floor_date(dateToLookup, "week") + hours(23) + minutes(59) + seconds(59)
after = ceiling_date(dateToLookup, "week") - days(1)
if((dateToLookup - before) < (after - dateToLookup)) {
before
} else {
after
}
}
> findClosestWeekendDay(ymd_hms("2014-12-13 13:33:29"))
[1] "2014-12-13 UTC"
> findClosestWeekendDay(ymd_hms("2014-12-14 18:33:29"))
[1] "2014-12-14 23:59:59 UTC"
> findClosestWeekendDay(ymd_hms("2014-12-15 18:33:29"))
[1] "2014-12-14 23:59:59 UTC"
> findClosestWeekendDay(ymd_hms("2014-12-17 11:33:29"))
[1] "2014-12-14 23:59:59 UTC"
> findClosestWeekendDay(ymd_hms("2014-12-17 13:33:29"))
[1] "2014-12-20 UTC"
> findClosestWeekendDay(ymd_hms("2014-12-19 13:33:29"))
[1] "2014-12-20 UTC"
I’ve set the Sunday date at 23:59:59 so that I can use this date in the next step where we want to calculate how how many hours it is from the current date to the nearest weekend.
I ended up with this function:
distanceFromWeekend = function(dateToLookup) {
before = floor_date(dateToLookup, "week") + hours(23) + minutes(59) + seconds(59)
after = ceiling_date(dateToLookup, "week") - days(1)
timeToBefore = dateToLookup - before
timeToAfter = after - dateToLookup
if(timeToBefore < 0 || timeToAfter < 0) {
0
} else {
if(timeToBefore < timeToAfter) {
timeToBefore / dhours(1)
} else {
timeToAfter / dhours(1)
}
}
}
> distanceFromWeekend(ymd_hms("2014-12-13 13:33:29"))
[1] 0
> distanceFromWeekend(ymd_hms("2014-12-14 18:33:29"))
[1] 0
> distanceFromWeekend(ymd_hms("2014-12-15 18:33:29"))
[1] 18.55833
> distanceFromWeekend(ymd_hms("2014-12-17 11:33:29"))
[1] 59.55833
> distanceFromWeekend(ymd_hms("2014-12-17 13:33:29"))
[1] 58.44194
> distanceFromWeekend(ymd_hms("2014-12-19 13:33:29"))
[1] 10.44194
While this works it’s quite slow when you run it over a data frame which contains a lot of rows.
There must be a clever R way of doing the same thing (perhaps using matrices) which I haven’t figured out yet so if you know how to speed it up do let me know.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.