Search code examples
rxtsrollapply

Using rollapply width parameter within FUN with unexpected result


I was using rollapply for a calculation that takes the last value in the roll and divides it by the mean minus one, which works fine as you can try for yourself:

set.seed(123)
v <- xts(rnorm(5, 5, 1), Sys.Date()-5:1)
rollapplyr(v, width = 3, function(x) x[3, ] / mean(x) - 1)

                  [,1]
2019-01-12          NA
2019-01-13          NA
2019-01-14  0.24784729
2019-01-15 -0.07241364
2019-01-16 -0.08178780

Then, I also needed to run the function with another parameter, eg. width = 4. Of cause, the function needs to be adjusted too:

rollapplyr(v, width = 4, function(x) x[4, ] / mean(x) - 1)

                  [,1]
2019-01-12          NA
2019-01-13          NA
2019-01-14          NA
2019-01-15 -0.02670674
2019-01-16 -0.04696956

To be more flexible, I tried passing the width parameter directly into the function and got a result I did not expect, although the fourth column is correct:

rollapplyr(v, width = 4, function(x, width) x[width, ] / mean(x) - 1)

                 [,1]        [,2]        [,3]        [,4]
2019-01-12         NA          NA          NA          NA
2019-01-13         NA          NA          NA          NA
2019-01-14         NA          NA          NA          NA
2019-01-15 -0.1478253 -0.08442393  0.25895593 -0.02670674
2019-01-16 -0.1137588  0.21861923 -0.05789086 -0.04696956

Would anyone understand what is conceptually wrong with using the width parameter in FUN and how can the output be explained? Anyone has an idea how to do it right?


Solution

  • The result you get is equal to the result from this line:

    rollapplyr(v, width = 4, function(x) x / mean(x) - 1)
    

    What happens internally when you have only 1 column is that you end up with teh lines of code below and some variables which are set. The outcome of setting the variables are already done in the code. The rollapply function is slightly more complicated.

    width <- 4
    ind <- as.matrix(seq.int(4, 5))
    # FUN passed on from rollappy
    FUN <- match.fun(function(x, width) x[width, ] / mean(x) - 1)
    sapply(ind, function(i) FUN(.subset_xts(v, 
                                            (i - width + 1):i)))
    

    After these lines of code is the build-up to the xts that is returned.

    But once you start debugging what is going on in the sapply part, (debug FUN) you can see that width is not passed on from the sapply call to function(i) and hence is not available when FUN is executed. Only where width is defined inside rollapply is width available for the .subset function. The same happens if you run the above lines of code. This is a result from the environment in which the width variable has been defined and in which environment the FUN is executed. These are different, which leads to the results you got.

    The best way forward is to wrap the rollapply inside another function like you mentioned in your comment:

    function(v,w) {
      rollapplyr(v, 
                 width = w, 
                 function(x) x[w, ] / mean(x) - 1)
    }
    

    Here the w is defined on a higher environment level and passed on correctly when the FUN inside rollapply is created and later executed in the sapply

    More info on environments you can find here in the advanced R book.