Reputation: 6799
I want to aggregate my dates on the monthly level. I would like to use the last Saturday of a certain month as the date for that month. I can get the date of a Saturday in a week by doing:
as.Date(paste(6, week(mdy(mydate)), year(mdy(mydate)), sep="-"), "%u-%W-%Y")
but months have different numbers of days, so I cant just do:
as.Date(paste(6, month(mdy(mydate)), year(mdy(mydate)), sep="-"), "%U-%m-%Y")
This doesn't even work, even if I was just trying to get the date of the 6th day of a month.
How can I get the date of the last Saturday of a month? So given a date 09-15-2014
I would get 09-27-2014
.
Upvotes: 2
Views: 1092
Reputation: 269441
1) zoo/cut In the zoo Quick Reference vignette this function appears which given a "Date"
class variable, x
, returns the same date if its Friday or the next Friday if not:
library(zoo)
nextfri <- function(x) 7 * ceiling(as.numeric(x-5+4) / 7) + as.Date(5-4)
Replacing the 5 with 6 will give the next Saturday
nextsat <- function(x) 7 * ceiling(as.numeric(x-6+4) / 7) + as.Date(6-4)
Now if x
is the input and is of Date class, get the first of its month using cut
, then get the first of the next month using cut
again, find the next Saturday using nextsat
and then subtract 7 to get the last Saturday of the input date's month.
the.first <- as.Date(cut(x, "month"))
next.month <- as.Date(cut(the.first + 32, "month")
nextsat(next.month) - 7
To test is out:
library(zoo)
x <- as.Date("2014-09-15")
nextsat <- function(x) 7 * ceiling(as.numeric(x-6+4) / 7) + as.Date(6-4)
the.first <- as.Date(cut(x, "month"))
next.month <- as.Date(cut(the.first + 32, "month"))
nextsat(next.month) - 7
## [1] "2014-09-27"
This only uses vectorized functions so if x
were a vector of dates it would still work.
1a) zoo/as.yearmon.Date/as.Date.yearmon We can shorten this by using the fact that as.Date(as.yearmon(x), frac = 1)
is the date of the last day of the month where as.yearmon.Date
and as.Date.yearmon
are zoo methods:
library(zoo)
x <- as.Date("2014-09-15")
nextsat <- function(x) 7 * ceiling(as.numeric(x-6+4) / 7) + as.Date(6-4)
nextsat(as.Date(as.yearmon(x), frac = 1) + 1) - 7
## [1] "2014-09-27"
This is also vectorized.
2) zoo/lubridate The above did not use lubridate but we can rework (1) to use lubridate like this:
library(zoo)
library(lubridate)
nextsat <- function(x) 7 * ceiling(as.numeric(x-6+4) / 7) + as.Date(6-4)
x <- as.Date("2014-09-15")
xx <- x
day(xx) <- 1
month(xx) <- month(xx) + 1
nextsat(xx) - 7
## [1] "2014-09-27"
This is also vectorized.
Upvotes: 5
Reputation: 93813
Using standard R date functions:
x <- as.Date(c("09-15-2014","09-15-2014"),format="%m-%d-%Y")
lastsat <- function(x,day) {
bits <- sapply(x, function(i) {
res <- seq.Date(as.Date(format(i,"%Y-%m-01")),length=2,by="1 month")[2] - (1:7)
res[format(res, "%u") == as.character(day)]
})
as.Date(bits, origin="1970-01-01")
}
lastsat(x,6)
#[1] "2014-09-27" "2014-09-27"
Upvotes: 4