Using lapply with Dates and the minus function

556 views Asked by At

I have a vector of dates and a vector of number-of-days.

dates <- seq(Sys.Date(), Sys.Date()+10, by='day')
number.of.days <- 1:4

I need to get a list with an entry for each number-of-days, where each entry in the list is the dates vector minus the corresponding number-of-days, ie.,

list(dates-1, dates-2, dates-3, dates-4)

The defintion of - (function (e1, e2) .Primitive("-")) indicates that its first and second arguments are e1 and e2, respectively. So the following should work.

lapply(number.of.days, `-`, e1=dates)

But it raises an error.

Error in -.Date(X[[i]], ...) : can only subtract from "Date" objects

Furthermore, the following does work:

lapply(number.of.days, function(e1, e2) e1 - e2, e1=dates)

Is this a feature or a bug?

2

There are 2 answers

1
BrodieG On BEST ANSWER

You can use:

lapply(number.of.days, `-.Date`, e1=dates)

Part of the problem is - is a primitive which doesn't do argument matching. Notice how these are the same:

> `-`(e1=5, e2=3)
[1] 2
> `-`(e2=5, e1=3)
[1] 2

From R Language Definition:

This subsection applies to closures but not to primitive functions. The latter typically ignore tags and do positional matching, but their help pages should be consulted for exceptions, which include log, round, signif, rep and seq.int.

So in your case, you end up using dates as the second argument to - even though you attempt to specify it as the first. By using the "Date" method for -, which is not a primitive, we can get it to work.

So technically, the behavior you are seeing is a feature, or perhaps a "documented inconsistency". The part that could possibly considered a bug is that R will do a multiple dispatch to a "Date" method for - despite that method not supporting non-date arguments as the first argument:

> 1:4 - dates  # dispatches to `-.Date` despite first argument not being date
Error in `-.Date`(1:4, dates) : can only subtract from "Date" objects
0
Dalton Hance On

You might be better off using POSIXt dates. They're a bit more flexible, for example if you wanted to add a week or year. The equivalent answer to @BrodieG using lubridate functionality to work with POSIXt:

dates <- ymd(seq(Sys.Date(), Sys.Date()+10, by='day'))
number.of.days <- 1:4

list(dates-1, dates-2, dates-3, dates-4)

lapply(number.of.days, `-.POSIXt`, e1=dates)

Also, how's the fishing in Philly? :)