Search code examples
rzoorollapply

Pass the `width` parameter of `zoo:rollapply` as argument to the called function


How can I pass the width argument of the zoo:rollapply function to the FUN function called within rollapply?

I put the values and widths as columns of a same data.frame:

library(zoo)
a = data.frame(v = 1:10, w = c(2,1,3,5,2,7,3,2,1,3))
#     v w
# 1   1 2
# 2   2 1
# 3   3 3
# 4   4 5
# 5   5 2
# 6   6 7
# 7   7 3
# 8   8 2
# 9   9 1
# 10 10 3

I can do:

rollapply(a$v, a$w, function(x) sum(x), partial=T)

Which gives:

[1] 3 2 9 20 11 42 21 17 9 19

Now I would like to use, for each rolling window of a$v, the corresponding value of a$w in the computation. For example:

rollapply(a$v, a$w, function(x) sum(x) + a$w[1], partial=T)

But instead of having a$w[1] as a constant value (here, it simply adds 2 to the values above), I would like to use the value in a$w corresponding each time to the a$w (i.e., in the same row of a).

Hence the desired output would be:

[1] 5 3 12 25 13 49 24 19 10 22


Solution

  • 1) The width need not be in the function. It can be added on afterwards:

    rollapply(a$v, a$w, sum, partial = TRUE) + a$w
    ## [1]  5  3 12 25 13 49 24 19 10 22
    

    Specify align= if you want different alignment.

    2) This approach is a bit ugly but another way is to maintain an external index.

    i <- 0
    rollapply(a$v, a$w, function(x) sum(x) + a$w[i <<- i+1], partial = TRUE)
    ## [1]  5  3 12 25 13 49 24 19 10 22
    

    2a) This can be cleaned up a bit at the expense of extra code using object oriented ideas to maintain state. Here we define a proto object p having an internal counter and an incr method which increments and returns it each time incr is invoked on the object.

    library(proto)
    p <- proto(counter = 0, incr = function(.) .$counter <- .$counter + 1)
    rollapply(a$v, a$w, function(x) sum(x) + a$w[p$incr()], partial = TRUE)
    ## [1]  5  3 12 25 13 49 24 19 10 22
    

    3) The example in the question used center alignment but if you actually need right or left alignment then it can be done by iterating over a rather than just over a$v. Here it is for right alignment.

    Sum <- function(x) {
      x <- matrix(x,,2)
      v <- x[, 1]
      w <- tail(x[, 2], 1)
      sum(v) + w
    }
    rollapplyr(a, a$w, Sum, partial = TRUE, by.column = FALSE)
    ## [1]  3  3  9 15 11 28 21 17 10 30
    
    # double check
    rollapplyr(a$v, a$w, sum, partial = TRUE) + a$w
    ## [1]  3  3  9 15 11 28 21 17 10 30