Search code examples
rzoo

How to perform a moving scale function over multiple columns in R?


I'm trying to calculate a z-score over a moving window of 8 observations using zoo::rollapply. My example code below is what I'm attempting to implement, but the end result is not right.

The only caveat to this is my real data has some NA values which I wish to ignore. I'd also prefer the window to increase in size until it hits 8 observations.

library(zoo)

df <- as.data.frame(
  matrix(round(runif(n = 120, min = 1, max = 20), 0), nrow = 20)
  )

z_score <- function(x){
  as.numeric(round(scale(x), 2))
}

df_scaled <- df %>%
  mutate_at(
    vars(1:6),
    ~rollapply(.x, width = 8, FUN = z_score, by.column = TRUE, partial = TRUE,
                          align = "right"
               )
    )

Solution

  • z_score should be returning a scalar. Note use of last. Also scale already removes NA's when computing the mean and sd.

    library(dplyr)
    library(zoo)
    set.seed(123) # for reproducibility
    
    df <- as.data.frame(
      matrix(round(runif(n = 120, min = 1, max = 20), 0), nrow = 20)
    )
    
    z_score <- function(x) c(last(round(scale(x), 2)))
    
    df %>%
      mutate(across(everything(), ~ rollapplyr(.x, 8, z_score, partial = TRUE)))
    

    or

    df %>% rollapplyr(8, z_score, partial = TRUE) %>% as.data.frame
    

    Either gives

          V1    V2    V3    V4    V5    V6
    1    NaN   NaN   NaN   NaN   NaN   NaN
    2   0.71 -0.71  0.71 -0.71  0.71 -0.71
    3  -0.26 -0.76  0.58 -0.06 -0.16  0.13
    4   1.01  1.13  0.21 -0.38  1.04  1.37
    5   0.94 -0.81 -1.08  1.21 -1.22 -0.36
    6  -1.38 -0.45 -0.90  0.10 -0.10  1.11
    7  -0.09 -1.18 -0.47  1.09  1.51  0.95
    8   0.89 -0.77  1.29  0.95  1.02 -0.08
    9  -0.34 -1.78 -0.36  0.87  0.79 -0.85
    10 -0.39 -1.55  2.11 -0.73 -1.21 -1.61
    11  0.91  1.19 -1.05  0.50 -1.13  0.93
    12 -0.43  1.13  0.39 -0.30  0.28 -0.93
    13  0.40  0.33  1.34  0.14 -0.55 -1.38
    14 -0.32  0.65 -0.98 -2.21  0.13  1.11
    15 -1.81 -1.51  0.47 -0.35 -0.60  0.61
    16  1.16 -0.15 -0.65 -1.03 -0.73 -0.85
    17 -1.00  0.44 -0.89 -0.29  1.53  0.13
    18 -1.32 -1.13  1.21  0.64 -1.11  1.00
    19 -0.36 -0.63  1.31 -0.22  0.14  0.14
    20  1.35 -0.75 -0.33 -1.03  0.44 -0.38