I have difficulty switching between data frames and zoo objects, particularly keeping meaningful column names, and inconsistencies between univariate and multivariate cases:
library(zoo)
#sample data, two species counts over time
t = as.Date(c("2012-01-01", "2012-01-02", "2012-01-03", "2012-01-04"))
n1 = c(4, 5, 9, 7) #counts of Lepisma saccharina
n2 = c(2, 6, 0, 11) #counts of Thermobia domestica
df = data.frame(t, n1, n2)
colnames(df) <- c("Date", "Lepisma saccharina", "Thermobia domestica")
#converting to zoo loses column names in univariate case...
> z1 <- read.zoo(df[,1:2]) #time series for L. saccharina
> colnames(z1)
NULL
> colnames(z1) <- c("Lepisma saccharina") #can't even set column name manually
Error in `colnames<-`(`*tmp*`, value = "Lepisma saccharina") :
attempt to set colnames on object with less than two dimensions
#... but not in multivariate case
> z2 <- read.zoo(df) #time series for both species
> colnames(z2)
[1] "Lepisma saccharina" "Thermobia domestica"
To go back from a zoo object to a data frame in the original format, it's not enough to use as.data.frame
since it won't include a Date column (the dates end up in the rownames): more work is needed.
zooToDf <- function(z) {
df <- as.data.frame(z)
df$Date <- time(z) #create a Date column
rownames(df) <- NULL #so row names not filled with dates
df <- df[,c(ncol(df), 1:(ncol(df)-1))] #reorder columns so Date first
return(df)
}
This works great on the multivariate case, but clearly can't recover a meaningful column name in the univariate case:
> df2b <- zooToDf(z2)
> df2b
Date Lepisma saccharina Thermobia domestica
1 2012-01-01 4 2
2 2012-01-02 5 6
3 2012-01-03 9 0
4 2012-01-04 7 11
> df1b <- zooToDf(z1)
> df1b
Date z
1 2012-01-01 4
2 2012-01-02 5
3 2012-01-03 9
4 2012-01-04 7
Is there a simple way to handle both univariate and multivariate cases? It seems z1
needs to remember the column name somehow.
If you don't want to drop dimensions, use drop=FALSE
:
R> (z1 <- read.zoo(df[,1:2], drop=FALSE))
Lepisma saccharina
2012-01-01 4
2012-01-02 5
2012-01-03 9
2012-01-04 7
You can do something like write.zoo
if you want to include the zoo index as a column in your data.frame:
zoo.to.data.frame <- function(x, index.name="Date") {
stopifnot(is.zoo(x))
xn <- if(is.null(dim(x))) deparse(substitute(x)) else colnames(x)
setNames(data.frame(index(x), x, row.names=NULL), c(index.name,xn))
}
UPDATE:
After trying to edit your question for brevity, I thought of an easy way to create df2b
to your specifications (this will also work for z1
if you don't drop dimensions):
R> (df2b <- data.frame(Date=time(z2), z2, check.names=FALSE, row.names=NULL))
Date Lepisma saccharina Thermobia domestica
1 2012-01-01 4 2
2 2012-01-02 5 6
3 2012-01-03 9 0
4 2012-01-04 7 11