CoolGuyHasChillDay
CoolGuyHasChillDay

Reputation: 747

Zoo::Rollmax How to shorten width to prevent errors

I have 10 days of values, and for each day I want to know the max of the previous 4 days. If there aren't 4 days worth of values, then I want the max of the last 3 days, etc. Code example:

set.seed(131)
Index <- 1:10
Val <- c(sample(10, 10, replace = T))
df = data.frame(Index, Val)

dfoo = df %>%
  mutate(Lag1 = lag(Val, 1, default = 0), #get last days value
         Last4Max = rollmax(Lag1, 4, partial = T, fill = 0, align = "right")) #get max of last 4 days

This works for all but for day 2/3 since there aren't 4 values in Lag1 (day 1 should be 0/NA because there's no "previous" day).

   Index Val Lag1    Last4Max
1      1   3    0        0
2      2   2    3        0
3      3   3    2        0
4      4   4    3        3
5      5   9    4        4
6      6   6    9        9
7      7   6    6        9
8      8   3    6        9
9      9   4    3        9
10    10  10    4        6

So Last4Max should be 3 for index 2/3, and 0/NA for 1. Is there a way to change the width size to account for having width>rownumbers? My alternative is to create 4 variables for each lag (with default = 0) and then take the max of all 4. I know this would work but it seems clunky, and it'd limit me if I wanted to quickly do max of last 10 days on a bigger dataset.

Thanks

Upvotes: 2

Views: 137

Answers (1)

G. Grothendieck
G. Grothendieck

Reputation: 269654

1) Note that:

  • as per ?rollmax it does not have a partial argument; however, we can use rollapply or rollapplyr with a partial argument and specify FUN = max.
  • rollapplyr (and also rollmaxr) with an r on the end defaults to align = "right" allowing one to avoid writing that argument out
  • the width argument can specify a one-component list of offsets so to specify that the prior 4 elements are to be used we can specify width = list(-seq(4)) eliminating the need for a separate lag column.

Putting all these together we get:

rollapplyr(Val, list(-seq(4)), max, partial = TRUE, fill = 0)
## [1] 0 3 3 3 4 9 9 9 9 6

2) Another way to do this is to use a width of 5 but not use the last element when taking the maximum. In this case we don't need fill = 0 since it is able to process each component of Val leaving nothing to fill.

Max <- function(x) if (length(x) > 1) max(head(x, -1)) else 0
rollapplyr(Val, 5, Max, partial = TRUE)

2a) If we knew that all elements of Val were non-negative then we could alternately use this shorter definition for Max:

Max <- function(x) max(head(x, -1), 0)

Upvotes: 3

Related Questions