How to make an empty vector of POSIXct
Asked Answered
J

7

32

I want to make an empty vector of POSIXct so that I can put a POSIXct in it:

vec <- vector("POSIXct", 10)
vec
vec[1] <- "2014-10-27 18:11:36 PDT"
vec

That does not work. Any ideas?

Jessamine answered 21/11, 2014 at 0:25 Comment(2)
I'm a fan of initializing to NA, as.POSIXct(rep(NA, 10))Valentinvalentina
@Gregor - would you like to post that as an answer? There is some valid concern about mine regarding mathematical operations on the result.Sackey
S
29

Since there is no POSIX mode, you cannot initialize a POSIXct vector with vector() alone (see ?mode for a list of all mode types).

But we can use .POSIXct to create the vector from a character vector.

(x <- .POSIXct(character(10))) ## the same as .POSIXct(vector("character", 10))
# [1] NA NA NA NA NA NA NA NA NA NA
class(x)
# [1] "POSIXct" "POSIXt" 

Also note that you can also use .POSIXct(integer(10)) for a length 10 vector of origin date-times.

Sackey answered 21/11, 2014 at 0:45 Comment(7)
This is something I have never tried before. Interesting! +1Geodetic
This is neat, but it relies on a parsing fallthrough in .POSIXct. Simpler (and possibly clearer) would be .POSIXct(rep(NA, 10))Tantalite
Parsing fallthrough? Not sure what that means, .POSIXct() just uses structure(). There are many ways. You could also do .POSIXct(NA[1:10])Sackey
By parsing fallthrough I mean that character(10) generates a vector of 10 empty strings, not 10 NA's, so the resulting structure works because "" gets printed as NA. The rep solution generates a logical vector, which will be promoted to numeric. This means that operations like x + 3600 will give strange errors.Tantalite
It's a good point you make. Mathematical operations would require us to change the mode. I will leave this answer as-is because the question was basic and the rep(NA, 10) method was already mentioned in the OP comments and upvoted a few timesSackey
For future viewers, I will note that I did include .POSIXct(integer(10)) which can be used in mathematical operations although not explicitly stated in the answer.Sackey
... and if you wanted to initialize as a double with a missing value that is .POSIXct(rep(NA_real_, length(x))) where length(x) is an integer indicating the length of the desired vector or NA_integer_ so that you initialize with the data-type of your choice but that the vector is filled with NA to start.Bidwell
V
11

I usually initialize things to NA:

as.POSIXct(rep(NA, 10))

works well in this case. It's explicitly doing what happens under-the-hood in @RichardScriven's answer---see the comments there for a longer discussion.

Valentinvalentina answered 7/10, 2015 at 21:28 Comment(0)
A
6

This question now has an incredibly simple answer!

lubridate allows you to simply write, e.g., empty_df <- tibble(date = POSIXct())

Assignation answered 12/7, 2021 at 20:13 Comment(0)
C
1

When creating a POSIXct vector in the following way, the underlying type becomes double:

> times <- as.POSIXct(c("2015-09-18 09:01:05.984 CEST", "2015-09-18 10:01:10.984 CEST", "2015-09-18 10:21:20.584 CEST"))
> typeof(times)
[1] "double"
> values <- c(5,6,7)

Combining the above vector with an empty vector of POSIXct initialized with character as the underlying type, results in a character-POSIXct vector:

> tm1 <- c(.POSIXct(character(0)), times)
> typeof(tm1)
[1] "character"

... which cannot be plotted directly:

> ggplot() + geom_line(aes(x=tm1, y=val), data=data.frame(tm1,val))
geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?

I therefore prefer initializing my empty POSIXct vectors with double or integer as the underlying type:

> tm2 <- c(.POSIXct(double(0)), times)
> typeof(tm2)
[1] "double"
> ggplot() + geom_line(aes(x=tm2, y=val), data=data.frame(tm2,val))

Simple POSIXct plot

> tm3 <- c(.POSIXct(integer(0)), times)
> typeof(tm3)
[1] "double"
> ggplot() + geom_line(aes(x=tm3, y=val), data=data.frame(tm3,val))
#Same thing...

When using double, the vector is also initialized with valid dates (which might or might not be preferable):

> .POSIXct(character(10))
 [1] NA NA NA NA NA NA NA NA NA NA
> .POSIXct(double(10))
 [1] "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET"
 [7] "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET" "1970-01-01 01:00:00 CET"
Carter answered 7/10, 2015 at 19:18 Comment(0)
D
1

I would go for Gregor's solution. I first went with Rich Scriven's solution but then got an error when I tried to compute the difference for a Non-NA element later as shown in the example below

t1 <- as.POSIXct("2014-10-27 18:11:36 PDT")
t2 <- as.POSIXct("2014-11-20 18:11:36 PDT")
x <- .POSIXct(character(10))
x[1] <- t1

difftime(t2, t1)
#R Time difference of 24 days

# fails
difftime(t2, x[1])
#R Error in unclass(time1) - unclass(time2) : 
#R   non-numeric argument to binary operator

unclass(x[1]) # character
#R [1] "1414429896"
unclass(t1)
#R [1] 1414429896
#R attr(,"tzone")
#R [1] ""

x <- .POSIXct(rep(NA_real_, 10))
x[1] <- t1
difftime(t2, x[1]) # all good
#R Time difference of 24 days

This can even lead to strange bugs like this one which can take a while to discover

t1 <- as.POSIXct("2001-07-24 CEST")
t2 <- as.POSIXct("2002-08-29 CEST")
x <- .POSIXct(character(10))
x[1] <- t1

t2 < t1
#R [1] FALSE
t2 < x[1] # oh boy 
#R [1] TRUE

# the reason (I think)
unclass(t2)
#R [1] 1030572000
#R attr(,"tzone")
#R [1] ""
unclass(x[1])
#R [1] "995925600"

"995925600" > 1030572000
#R [1] TRUE
Dead answered 1/2, 2019 at 16:50 Comment(0)
T
0

I use the following function for this. Very similar to the other solutions.

vector_datetime <- function(n = 0L) structure(rep(NA_integer_, n), class = c("POSIXct", "POSIXt"))

So you can do things like this.

> vector_datetime()
POSIXct of length 0

> vector_datetime(10)
 [1] NA NA NA NA NA NA NA NA NA NA

> class(vector_datetime(10))
[1] "POSIXct" "POSIXt" 

This can also be done with lubridate.

library(lubridate)

> as_datetime(integer(0))
POSIXct of length 0
Trant answered 22/9, 2020 at 15:59 Comment(0)
C
0

Perhaps I missed it above, but this is a truly empty POSIXct:

as.POSIXct(integer())

For example, if you want an empty data frame:

empty <- data.frame(date_time = as.POSIXct(integer()),
                    date = as.Date(x = integer(), origin = "1970-01-01"))
empty
[1] date_time date     
<0 rows> (or 0-length row.names)

str(empty)
'data.frame':   0 obs. of  2 variables:
 $ date_time: 'POSIXct' num(0) 
 - attr(*, "tzone")= chr ""
 $ date     : 'Date' num(0) 
Coruscation answered 22/12, 2020 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.