2
votes

If I have a date, say "2014-05-13" and I want to calculate the month in decimal, I would do this:

5 + 13/31 = 5.419355

How would it be possible in R to take a vector of dates and turn in it into a "month decimal" vector?

For example:

dates = c("2010-01-24", "2013-04-08", "2014-03-05", "2013-03-08", "2014-02-14", 
          "2004-01-28", "2006-02-21", "2013-03-28", "2013-04-01", "2006-02-14", 
          "2006-01-28", "2014-01-19", "2012-03-12", "2014-01-30", "2005-04-17")

library(lubridate)
month(dates) + day(dates)/31

As you can see, it would be wrong to put "31" as the diviser since the number of days differ depending on the month, and sometimes year (leap years).

So what would be the best solution?

3

3 Answers

7
votes

You can use monthDaysfunction from Hmisc package

> require(Hmisc)
> library(lubridate)
> month(dates) + day(dates)/monthDays(dates)
 [1] 1.774194 4.266667 3.161290 3.258065 2.500000 1.903226 2.750000 3.903226 4.033333
[10] 2.500000 1.903226 1.612903 3.387097 1.967742 4.566667
6
votes

With magrittr,

library(magrittr)
library(lubridate)

dates %>% ymd() %>% { month(.) + day(.) / days_in_month(.) }

##      Jan      Apr      Mar      Mar      Feb      Jan      Feb      Mar      Apr      Feb      Jan 
## 1.774194 4.266667 3.161290 3.258065 2.500000 1.903226 2.750000 3.903226 4.033333 2.500000 1.903226 
##      Jan      Mar      Jan      Apr 
## 1.612903 3.387097 1.967742 4.566667 

For some reason the vector gets named, so add %>% unname() if you like.

6
votes

Here is a base R hack that uses a trick I've seen on SO to get the first day of the next month and subtract 1 to return the last day of the month of interest.

# format dates to Date class
dates <- as.Date(dates)
# get the next month
nextMonths <- as.integer(substr(dates, 6, 7)) + 1L
# replace next month with 1 if it is equal to 13
nextMonths[nextMonths == 13] <- 1L

# extract the number of days using date formatting (%d), paste, and subtraction
dayCount <- as.integer(format(as.Date(paste(substr(dates, 1, 4),
                                      nextMonths, "01", sep="-"))-1L, format="%d"))

dayCount
[1] 31 30 31 31 28 31 28 31 30 28 31 31 31 31 30

# get month with fraction using date formatting (%m)
as.integer(format(dates, format="%m")) + (as.integer(format(dates, format="%d")) / dayCount)
[1] 1.774194 4.266667 3.161290 3.258065 2.500000 1.903226 2.750000 3.903226 4.033333 2.500000
[11] 1.903226 1.612903 3.387097 1.967742 4.566667