joshuaulrich / xts

Extensible time series class that provides uniform handling of many R time series classes by extending zoo.
http://joshuaulrich.github.io/xts/
GNU General Public License v2.0
220 stars 71 forks source link

Add rbind support for heterogenous columns #329

Open TravisLeithVeloqx opened 4 years ago

TravisLeithVeloqx commented 4 years ago

Description

I would like to be able to rbind multiple xts objects with possibly different columns.

Expected behavior

From here

All rows and all columns should be present with NA (or some other fill value) where no data was found.

As an extension (not given in Josh's SO solution), intersections should be handled with a user supplied function, like max or mean etc.

I personally think this is a natural fit for the xts package, rather than having users design their own solution similar to the link.

joshuaulrich commented 4 years ago

I like this idea, though I'm not sure when I would get around to implement it.

joshuaulrich commented 4 years ago

Here's a reproducible example of the code in the stackoverflow Q&A the OP linked to.

# put all xts objects in a list for easier processing
x <- list(
  z1 = xts(t(c("9902"=0,"9903"=0,"9904"=0,"9905"=2,"9906"=2)),as.Date("2015-01-01")),
  z2 = xts(t(c("9902"=3,"9903"=4,"9905"=6,"9906"=5,"9908"=8)),as.Date("2015-01-02")),
  z3 = xts(t(c("9901"=1,"9903"=3,"9905"=5,"9906"=6,"9907"=7,"9909"=9)),as.Date("2015-01-03"))
)

# function to create template xts object
template <- function(xlist) {
  # find set of unique column names from all objects
  cn <- unique(unlist(lapply(xlist, colnames)))
  # create template xts object
  # using a date that doesn't occur in the actual data
  minIndex <- do.call(min, lapply(xlist, function(x) index(x[1L,])))
  # template object
  xts(matrix(0,1,length(cn)), minIndex-1, dimnames=list(NULL, sort(cn)))
}

# function to apply to each xts object
proc <- function(x, template) {
  # columns we need to add
  neededCols <- !(colnames(template) %in% colnames(x))
  # merge this object with template object, filling w/zeros
  out <- merge(x, template[,neededCols], fill=0)
  # reorder columns (NB: merge.xts always uses make.names)
  # and remove first row (from template)
  out <- out[-1L,make.names(colnames(template))]
  # set column names back to desired values
  # (using attr<- because dimnames<-.xts copies)
  attr(out, "dimnames") <- list(NULL, colnames(template))
  # return object
  out
}
(res <- do.call(rbind, lapply(x, proc, template=template(x))))
#            9901 9902 9903 9904 9905 9906 9907 9908 9909
# 2015-01-01    0    0    0    0    2    2    0    0    0
# 2015-01-02    0    3    4    0    6    5    0    8    0
# 2015-01-03    1    0    3    0    5    6    7    0    9