Reputation: 4995
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
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
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
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
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
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