rgenericstime-seriesxtszoo

Matching R time series class between input and output


I have an R function that analyzes regular time series, originally written by assuming the data were just a vector, say of length N. It outputs a list of vectors that are either of length N or length N+1 adding a t=0 result. So here is a conceptual example:

tsf <- function(ts){
  extend <- c(0.5,ts)     # A time step before the first in ts
  twotimes <- 2.*ts
  return (list(extend=extend,twotimes=twotimes))
}

the function is intended for univariate geophysical series data that have a (possibly gappy) regular time index based on datetimes and a time step that is some number of minutes. I'm more fluent in Pandas/Python, so to me the playing field for time series classes in R seems wide, possibly including zoo, xts as well as just the time series class or even a data frame with POSIXct column. In xts:

My question what is the simplest (small code, low dependency) way to address the matching problem so that extend and twotimes are of the same class as ts. For instance given this data:

tseq <- seq(from = as.POSIXct('2005-01-01 00:00',tz=''), length.out = 5, by = "15 min")
x <- rnorm(5)
xdata <- xts(data=x,order.by=tseq)
zdata <- zooreg(data=x,order.by=tseq)

Could the following (which are not really functional):

tsf(x)
tsf(xdata)
tsf(zdata)

be made to both produce like output as their argument? Is the R way to create a tsf.zoo, tsf.xts etc and have these focus on incoming and outgoing coersion and the indexing? Will these type-specific functions cause issues if the user elects not to install one of the libraries like xts?


Solution

  • R uses object oriented programming which allows a generic function to act on different classes by defining methods for them. For * there are already methods defined for numeric, zooreg and xts classes and for extend that we can define a single default method that covers numeric and zooreg (and ts class) and define a separate methods for xts and data.frame.

    We have corrected the errors in the question in defining xdata and zdata -- data is not the name of the first argument, zooreg does not take an order.by argument and set.seed is needed to make tests using random numbers reproducible. See Note at end and relevant help files.

    library (xts) # also pulls in zoo
    
    tsf <- function(x) list(extend = extend(x), twotimes = 2 * x)
    
    extend <- function(x, ...) UseMethod("extend")
    extend.default <- function(x, ...)  replace(c(head(stats::lag(x), 1), x), 1, 0.5)
    extend.xts <- function(x, ...) as.xts(extend(as.zooreg(x)))
    extend.data.frame <- function(x, ...) {
      setNames(fortify.zoo(extend(as.zooreg(read.zoo(x)))), names(x))
    }
    
    # test
    tsf(x)
    tsf(xdata)
    tsf(zdata)
    tsf(ts(x))
    

    Another approach is to convert the input to zooreg, operate on it and convert back. In that case use this single method for numeric, zoo and xts and a separate method for data.frame.

    extend <- function(x, ...) UseMethod("extend")
    extend.default <- function(x, ...) {
      z <- as.zooreg(x)
      extend <- c(head(stats::lag(z), 1), x)
      extend[1] <- 0.5
      do.call(paste("as", data.class(x), sep = "."), list(extend))
    }
    extend.data.frame <- function(x, ...) {
      setNames(fortify.zoo(extend(as.zooreg(read.zoo(x)))), names(x))
    }
    

    Note

    library(xts)
    tseq <- seq(from = as.POSIXct('2005-01-01 00:00:00', tz = ''), length.out = 5,
      by = "15 min")
    set.seed(123)
    x <- rnorm(5)
    xdata <- xts(x, tseq)
    zdata <- as.zooreg(zoo(x, tseq))