3

I have a function in R that, given n days, returns a list of the last n weekdays. My solution works fine, but it feels inelegant, and I was wondering if there were any easy ways to improve it.

WeekdayList <- function(n) {
    Today <- as.Date(Sys.time())
    days <- c(Today)
    i <- 1
    while (length(days) < n) {
        NewDay <- as.Date(Today-i)
        if (!weekdays(NewDay) %in% c("Saturday", "Sunday")) {
            days <- c(days,NewDay)
        }
        i <- i+1
    }
    days
}

WeekdayList(30)
WeekdayList(2)

Exclusion of holidays would be a nice feature too.

Zach
  • 29,791
  • 35
  • 142
  • 201
  • You might find the answer in here: http://stackoverflow.com/questions/5046708/calculate-the-number-of-weekdays-between-2-dates-in-r – Sacha Epskamp May 24 '11 at 17:14
  • @Sacha Epskamp I think this covers different ground. In this case, I have a start date, but the end date is unknown. In the previous question, the start and end are both known. – Zach May 24 '11 at 17:18
  • If you have a start date and a number of days you have an end day right? – Sacha Epskamp May 24 '11 at 17:42
  • @Sacha Epskamp no, because it is unknown how many weekends you will encounter in your quest for exactly n weekdays. – Zach May 24 '11 at 18:04
  • Ah given n weekdays not n days then? – Sacha Epskamp May 24 '11 at 18:09

2 Answers2

6

Vectorizing code is essential in R. Here is the example:

WeekdayList2 <- function(n) {
  Today <- as.Date(Sys.time())
  dayz <- seq(Today, Today - 2 * n, "-1 days")
  dayz <- dayz[!(weekdays(dayz) %in% c("Saturday", "Sunday"))]
  dayz <- dayz[seq_len(n)]
  return(dayz)
}
identical(WeekdayList2(1000), WeekdayList(1000))
system.time(WeekdayList2(10000))
system.time(WeekdayList(10000))
[1] TRUE
   user  system elapsed 
      0       0       0 
   user  system elapsed 
   4.90    0.00    4.91 

As you can see, even though my function creates a vector twice almost twice the size it needs to be (and then deletes the weekends), it is much faster than using a "for" loop. My computer cannot even run your function with n = 100000 (not that that you'd care about that many days back anyway), but WeekdayList2 runs it almost instantly.

Since Holidays are relative to where you are, you'll probably need to manually upload a list of dates, and add another criteria to filter out those dates from the data.

Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
Rguy
  • 1,622
  • 1
  • 15
  • 20
  • 1
    @Thanks for the tip about vectorization. I'm away that I should vectorize whenever possible, but for this particular problem I was having trouble framing it in vectors, and ended up thinking in loops. – Zach May 24 '11 at 18:13
  • 1
    Fixed it for n = 0. `1:n` is dangerous! `seq_len` is good to know. – Richie Cotton May 25 '11 at 09:53
  • @ Richie can you please elaborate? When you plug in "0" to my original function, it just outputs today's date. Where could 1:n be dangerous? The worst that can happen is an error message, right? And thanks for whoever changed the sequence to be automatically reversed, I didn't know you could use strings like "-1 days" for the increment! – Rguy May 25 '11 at 16:22
4

I added a holiday calculation to Rguy's code.

WeekdayList3 <- function(n) {
    library(timeDate)
    Today <- as.Date(Sys.time())
    dayz <- rev(seq(Today - 2 * n, Today, "days"))
    years <- as.numeric(unique(format(dayz,'%Y')))
    holidays <- as.Date(holidayNYSE(years))
    dayz <- dayz[!(weekdays(dayz) %in% c("Saturday", "Sunday"))]
    dayz <- dayz[!(dayz %in% holidays)]
    dayz <- dayz[1 : n]
    return(dayz)
}

WeekdayList3(100)
Zach
  • 29,791
  • 35
  • 142
  • 201