tcash21
tcash21

Reputation: 4995

Using apply to convert dates in R and handling NA dates

This seems like it should be a lot easier and I'm sure someone can help me. I'm trying to change each date to the first of its respective month from a data.frame of dates using floor_date() in the lubridate package, however some of those dates are NAs. I'd rather not substitute dummy dates for the NAs.

I've tried the below:

library(lubridate)
a<-c(as.Date("2011-05-04"), as.Date("2011-06-12"))
b<-c(as.Date("2012-03-01"), NA)
test <- data.frame(a,b)

apply(test, 1, function(y) sapply(y, function(x) if(!is.na(x)) floor_date(x, "month") else na.pass(x)))
apply(test, 1, function(y) ifelse(!is.na(y)), floor_date(y, "month"), na.pass(y))

The first call returns:

Error in object[[name, exact = TRUE]] : subscript out of bounds

The second call returns:

Error in update.default(x, mdays = 1, hours = 0, minutes = 0, seconds = 0) : 
need an object with call component

Thank you for any help!

Upvotes: 4

Views: 13348

Answers (5)

Garrett
Garrett

Reputation: 61

The NA bug in floor_date() is fixed in lubridate 1.1.0 which was sent to CRAN today. An NA bug in the S3 update method for dates remains (fixed in the development version). In the mean time,

floor_date(as.POSIXlt(test$b), unit = "month")

would work.

Upvotes: 1

nograpes
nograpes

Reputation: 18323

If you wanted to do it in a one-liner like you were trying, this would work:

data.frame(lapply(test,function (y) (as.Date(sapply(y,function(x) if (is.na(x)) NA else floor_date(x,'month'))))))

The real problem here is the lubridate function itself, which should allow you to pass a parameter to update.Date telling it to ignore NA. The strftime solution above is definitely the cleanest.

Also, as mentioned in the comments, the reason why your solution didn't work was because you used apply instead of lapply.

Upvotes: 1

Josh O&#39;Brien
Josh O&#39;Brien

Reputation: 162401

I don't know about lubridate, but you could do this easily with the excellent date-handling facilities provided by base R.

Here's a little helper function that should perform the calculations you want without complaint:

firstOfMonth <- function(dates) {
    as.Date(strftime(dates, format="%Y-%m-01"))
}

firstOfMonth(a)
# [1] "2011-05-01" "2011-06-01"
firstOfMonth(b)
# [1] "2012-03-01" NA   

data.frame(lapply(test, firstOfMonth))
#            a          b
# 1 2011-05-01 2012-03-01
# 2 2011-06-01       <NA>

Upvotes: 7

b_rousseau
b_rousseau

Reputation: 169

Have you tried package zoo ?

library(zoo)
a<-c(as.Date("2011-05-04"), as.Date("2011-06-12"))
b<-c(as.Date("2012-03-01"), NA)
test <- data.frame(
        "a" = as.Date(as.yearmon(a)),
        "b" = as.Date(as.yearmon(b))
)

Upvotes: 2

Karsten W.
Karsten W.

Reputation: 18490

How about this?

my_floor_date <- function(x,...) {idx <- !is.na(x); x[idx] <- floor_date(x[idx], ...); x}
transform(test, a=my_floor_date(a, "month"), b=my_floor_date(b, "month"))

Upvotes: 1

Related Questions