Search code examples
rdate

R find last weekday of month


How do I find the last weekday (e.g., Wednesday) of the month using R? In the code below, I calculate the month, day of the month, week of the month, and weekday. There are 5 Wednesdays in January 2014, but only 4 Wednesdays in February 2014, so I cannot use max(week of the month) as a filter. Any help is appreciated although I prefer to use the base R functions.

DF <- data.frame(DATE = seq(as.Date("2014-01-01"), as.Date("2014-12-31"), "day"))

DF$MONTH         <- as.numeric(format(DF$DATE, "%m"))
DF$DAY_OF_MONTH  <- as.numeric(format(DF$DATE, "%d"))
DF$WEEK_OF_MONTH <- ceiling(as.numeric(format(DF$DATE, "%d")) / 7)
DF$WEEKDAY       <- format(DF$DATE, "%A")

DF

Solution

  • I think this is what you're after:

    DF$last_weekday_o_month <- ave( 
      weekdays(DF$DATE), 
      months(DF$DATE), 
      FUN = function(x) tail(x[ !(x %in% c("Saturday","Sunday")) ], 1) 
    )
    

    To find the particular date that is the last weekday....

    DF$last_weekdaydate_o_month <- ave( 
      DF$DATE, 
      months(DF$DATE), 
      FUN = function(x) tail(x[ !(weekdays(x) %in% c("Saturday","Sunday")) ], 1) 
    )
    

    the result looks like...

              DATE last_weekday_o_month last_weekdaydate_o_month
    1   2014-01-01               Friday               2014-01-31
    2   2014-01-02               Friday               2014-01-31
    3   2014-01-03               Friday               2014-01-31
    4   2014-01-04               Friday               2014-01-31
    5   2014-01-05               Friday               2014-01-31
    6   2014-01-06               Friday               2014-01-31
    ...
    360 2014-12-26            Wednesday               2014-12-31
    361 2014-12-27            Wednesday               2014-12-31
    362 2014-12-28            Wednesday               2014-12-31
    363 2014-12-29            Wednesday               2014-12-31
    364 2014-12-30            Wednesday               2014-12-31
    365 2014-12-31            Wednesday               2014-12-31
    

    If you did this first, of course you could compute last_weekday_o_month as weekdays(last_weekdaydate_o_month).


    With a couple packages, this can be done more elegantly/readably, as suggested by @RichardScriven:

    library(data.table)
    setDT(DF)[, 
      last_weekdaydate_o_month := last(DATE[!chron::is.weekend(DATE)])
    , by = month(DATE)]
    

    which gives

               DATE last_weekdaydate_o_month
      1: 2014-01-01               2014-01-31
      2: 2014-01-02               2014-01-31
      3: 2014-01-03               2014-01-31
      4: 2014-01-04               2014-01-31
      5: 2014-01-05               2014-01-31
     ---                                    
    361: 2014-12-27               2014-12-31
    362: 2014-12-28               2014-12-31
    363: 2014-12-29               2014-12-31
    364: 2014-12-30               2014-12-31
    365: 2014-12-31               2014-12-31