Reputation: 821
I have a vector with time (from an Excel file). It gets read in as a factor. This is a short version/example of it:
starttime <- factor(c("12:55:00", "13:45:00", "14:30:00", "10:00:00", "10:40:00", "12:00:00", "12:30:00"))
I wanted to round all these times to the nearest hour in military time and extract the hour as an integer, and I ran into issues trying to do it with a package or a base function. I used the lubridate
package, and tried:
round_date(hms(starttime), unit= "hour")
hms
seems to work fine on its own (I indeed get a my data as class Period), but then using it in combination with round_date
gives me the error
Error in as.POSIXct.numeric(x) : 'origin' must be supplied
I then tried the following (1904 because it's data from Excel/csv file on a Mac)
round_date(hms(starttime, origin="1904-01-01"), unit="hour")
but that gave me the exact same error, so I gathered I specified the origin in the wrong function. Next try was
round_date(hms(starttime), unit="hour"), origin="1904-01-01")
which gave me the error
Error in round_date(hms(starttime), unit = "hour", origin = "1904-01-01") : unused argument (origin = "1904-01-01")
I also tried base R:
round.Date(as.POSIXct(as.character(starttime), format="%H:%M:%S"), units="hours")
gave me
Error in NextMethod() : generic function not specified
Also here, the conversion of the vector to dates works, but the trouble starts when the rounding function is added.
Now, I do totally understand why this might not work using lubridate
, as there are no dates associated with the times (so the timer under the hood can't do its thing). I'm a little confused though about the error message that an origin is needed but then it is ignored. I can't use ymd_hms()
because there is no data in the vector for dates. And I can't figure out what the error for the base R function means at all - where there are actually dates added (the current date).
I ended up solving this manually:
ifelse(minute(hms(starttime))>=30, hour(hms(starttime))+1, hour(hms(starttime)))
Is there a way to do this with a function lubridate
or base R?
If not, any ideas why not? It seems to me like something that should be possible.
Upvotes: 3
Views: 4349
Reputation: 93938
Try a slight modification to what you have:
round(as.POSIXct(starttime, format="%H:%M:%S", tz="UTC"), units="hours")
#[1] "2018-06-27 13:00:00 UTC" "2018-06-27 14:00:00 UTC"
#[3] "2018-06-27 15:00:00 UTC" "2018-06-27 10:00:00 UTC"
#[5] "2018-06-27 11:00:00 UTC" "2018-06-27 12:00:00 UTC"
#[7] "2018-06-27 13:00:00 UTC"
R will automatically dispatch to the proper round.POSIXt
when you call round
with an as.POSIXct/as.POSIXlt
object. In your original code, you are forcing round
to try to work with a Date
object, which you don't have - you have a POSIXct
datetime object.
A Date
in R is generally just "year/month/day", no time component, as opposed to a POSIXct
which is a "year/month/day hh/mm/ss/fractional seconds." The two are not directly interchangeable or comparable. E.g.:
> Sys.time() == Sys.Date()
#[1] FALSE
#Warning message:
#Incompatible methods ("Ops.POSIXt", "Ops.Date") for "=="
(If you want to get technical, a Date
is stored as the number of days since 1970-01-01 - including fractional days, and a POSIXct
is the number of seconds since 1970-01-01:00:00:00 - including fractional seconds. A POSIXlt
is different again, and is a series of list
objects, with parts for day,month,year,minutes etc).
Upvotes: 5