Question

Consider the following R code which replaces the values in one column of a data frame with a set of POSIXct values:

foo <- as.data.frame(list(bar=rep(5,5)))
bar <- as.POSIXct(rep(5,5), origin="1970-1-1", tz="c")
foo[,1] <- bar

My question: why does the same operation fail when I try to use POSIXlt instead? For example:

foo <- as.data.frame(list(bar=rep(5,5)))
bar <- as.POSIXlt(rep(5,5), origin="1970-1-1", tz="c")
foo[,1] <- bar
Warning message:
   In `[<-.data.frame`(`*tmp*`, , 1, value = list(sec = c(5, 5, 5,  :
   provided 9 variables to replace 1 variables

Moreover, if I refer to the column by name, instead of by index, the same assignment works just fine:

foo$bar <- bar
foo <- as.data.frame(list(bar=rep(5,5)))
bar <- as.POSIXlt(rep(5,5), origin="1970-1-1", tz="c")
foo$bar <- bar

What am I missing?

Était-ce utile?

La solution

From help("[.data.frame"):

For [ the replacement value can be a list: each element of the list is used to replace (part of) one column, recycling the list as necessary.

So you can do things like this:

df <- data.frame(a=1:2)
df[,2:3] <- list(3:4, 5:6)
#  a V2 V3
#1 1  3  5
#2 2  4  6

However, this results in a warning:

df[,4] <- list(7, 8)
# Warning message:
#   In `[<-.data.frame`(`*tmp*`, , 4, value = list(7, 8)) :
#   provided 2 variables to replace 1 variables
#   a V2 V3 V4
# 1 1  3  5  7
# 2 2  4  6  7

Now a POSIXlt object is a list with 9 elements:

unclass(rep(as.POSIXlt(Sys.time()), 2))
# $sec
# [1] 1.958244 1.958244
# 
# $min
# [1] 54 54
# 
# $hour
# [1] 10 10
# 
# $mday
# [1] 4 4
# 
# $mon
# [1] 9 9
# 
# $year
# [1] 113 113
# 
# $wday
# [1] 5 5
# 
# $yday
# [1] 276 276
# 
# $isdst
# [1] 1 1
# 
# attr(,"tzone")
# [1] ""     "CET"  "CEST"

Assigning this list to one column using [<-.data.frame gives the warning you observed.

Potential solutions to the problem are simple:

  • Use POSIXct and avoid POSIXlt. The only reason for using the latter is a need for extracting some of the list components, which is not often the case (and you can always coerce to POSIXlt, e.g., inside functions for rounding of time values).
  • Use $<-.data.frame. This is often cumbersome outside of interactive use.
  • Wrap the POSIXlt object in a list for assignment: df[,1] <- list(POSIXlt_object).

Autres conseils

It appears that POSIXlt objects are lists, with 9 entries.

unlist(as.POSIXlt(5, origin="1970-1-1"))
sec   min  hour  mday   mon  year  wday  yday isdst 
  5     0     1     1     0    70     4     0     0

unlist(as.POSIXct(5, origin="1970-1-1"))
[1] "1970-01-01 00:00:05 CET"

Apparently the assignment to a data.frame fails because the POSIXlt object is unlisted.

foo <- as.data.frame(list(bar=rep(5,5)))
bar <- as.POSIXlt(rep(5,5), origin="1970-1-1", tz="")
foo[,1] <- bar   # this fails
foo[,1:9] <- bar # this works

On the other hand, the assignment to a list works.

foo <- as.data.frame(list(bar=rep(5,5)))
bar <- as.POSIXlt(rep(5,5), origin="1970-1-1", tz="")
foo[[1]] <- bar  # or foo$bar <- bar
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top