Question

Consider the following (example) zoo object:

example zoo object

The data frame is sorted in ascending order on the date index. The ma3 field provides the 3-day moving average value of the duration field. The comparison field compares the value of the duration value to the corresponding ma3 value; (1) IF duration > ma3 THEN 'ABOVE', (2) IF duration < ma3 THEN 'BELOW', (3) ELSE 'EQUAL'.

The values for the consec_day field are calculated as follows: Starting from the earliest date with a ma3 value if the comparison value equals 'ABOVE' then the sign of the consec_day value is positive, if the comparison value equals 'BELOW' then the sign of the consec_day value is negative, and if the comparison value equals 'EQUAL' then the consec_day value is zero. To determine the magnitude of the consec_day value, count the number of consecutive (from oldest to most recent), identical comparison values.

Questions:

  1. Can the computation of the consec_day field be vectorized?
  2. If so, how?

My current solution uses loops as follows:

    z0 <- zoo(matrix(c(c(345, 432, 112, 332, 496, 414, 211), c(NA, NA, 296.33, 292, 313.33, 414, 373.67), c(NA, NA, 'BELOW', 'ABOVE', 'ABOVE', 'EQUAL', 'BELOW'), c(NA, NA, -1, 1, 2, 0, -1)), nrow = 7, ncol = 4), seq(as.Date('2013-07-31'), as.Date('2013-08-06'), by = "day"))
    colnames(z0) <- c("duration", "ma3", "comparison", "consec_day")
    require(xts)
    for (r in 1:nrow(z0)) {
      if (is.na(z0$comparison[r])) {next}
      if (z0$comparison[r] == 'EQUAL') {z0$consec_day[r] <- 0; next}
      if (is.na(z0$comparison[r - 1])) {z0$consec_day[r] <- ifelse(z0$comparison[r] == 'ABOVE', 1, ifelse(z0$comparison[r] == 'BELOW', -1, 0)); next}
      if ( (xts::coredata(df0)[r, 3] != xts::coredata(df0)[r - 1, 3]) & xts::coredata(df0)[r, 3] == 'ABOVE') {
        df0$consec_day[r] <- 1 
      } else {
      if ( (xts::coredata(df0)[r, 3] != xts::coredata(df0)[r - 1, 3]) & xts::coredata(df0)[r, 3] == 'BELOW') {
        df0$consec_day[r] <- -1 
      } else {ifelse((xts::coredata(df0)[r, 3] != xts::coredata(df0)[r - 1, 3]) & xts::coredata(df0)[r, 3] == 'ABOVE')), df0$consec_day[r] <- df0$consec_day[r - 1] + 1, df0$consec_day[r] <- df0$consec_day[r - 1] - 1}
    }
Was it helpful?

Solution

Use run length encoding (rle)

You will need to pass an atomic vector (and replace the leading NA values with '.NA' as rle doesn't really deal well with them)

comparison <- z0[,3]
compAtomic <- as.character(comparison)
compAtomic[is.na(compAtomic)] <- '.NA'

# define your changes
changes <- c('BELOW' =-1, 'EQUAL' = 0, 'ABOVE' = 1, '.NA' = NA )
# perform rle (and unclass the results)
rrl <- unclass(rle(compAtomic))
# a bit of `rep` and `sequence`
with(rrl, sequence(lengths) * rep(changes[values],lengths))
#  .NA   .NA BELOW ABOVE ABOVE EQUAL BELOW 
#   NA    NA    -1     1     2     0    -1 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top