Calculating moving average

前端 未结 16 1792
夕颜
夕颜 2020-11-21 23:55

I\'m trying to use R to calculate the moving average over a series of values in a matrix. The normal R mailing list search hasn\'t been very helpful though. There doesn\'t s

相关标签:
16条回答
  • 2020-11-22 00:44

    In fact RcppRoll is very good.

    The code posted by cantdutchthis must be corrected in the fourth line to the window be fixed:

    ma <- function(arr, n=15){
      res = arr
      for(i in n:length(arr)){
        res[i] = mean(arr[(i-n+1):i])
      }
      res
    }
    

    Another way, which handles missings, is given here.

    A third way, improving cantdutchthis code to calculate partial averages or not, follows:

      ma <- function(x, n=2,parcial=TRUE){
      res = x #set the first values
    
      if (parcial==TRUE){
        for(i in 1:length(x)){
          t<-max(i-n+1,1)
          res[i] = mean(x[t:i])
        }
        res
    
      }else{
        for(i in 1:length(x)){
          t<-max(i-n+1,1)
          res[i] = mean(x[t:i])
        }
        res[-c(seq(1,n-1,1))] #remove the n-1 first,i.e., res[c(-3,-4,...)]
      }
    }
    
    0 讨论(0)
  • 2020-11-22 00:46

    Using cumsum should be sufficient and efficient. Assuming you have a vector x and you want a running sum of n numbers

    cx <- c(0,cumsum(x))
    rsum <- (cx[(n+1):length(cx)] - cx[1:(length(cx) - n)]) / n
    

    As pointed out in the comments by @mzuther, this assumes that there are no NAs in the data. to deal with those would require dividing each window by the number of non-NA values. Here's one way of doing that, incorporating the comment from @Ricardo Cruz:

    cx <- c(0, cumsum(ifelse(is.na(x), 0, x)))
    cn <- c(0, cumsum(ifelse(is.na(x), 0, 1)))
    rx <- cx[(n+1):length(cx)] - cx[1:(length(cx) - n)]
    rn <- cn[(n+1):length(cx)] - cn[1:(length(cx) - n)]
    rsum <- rx / rn
    

    This still has the issue that if all the values in the window are NAs then there will be a division by zero error.

    0 讨论(0)
  • 2020-11-22 00:47

    EDIT: took great joy in adding the side parameter, for a moving average (or sum, or ...) of e.g. the past 7 days of a Date vector.


    For people just wanting to calculate this themselves, it's nothing more than:

    # x = vector with numeric data
    # w = window length
    y <- numeric(length = length(x))
    
    for (i in seq_len(length(x))) {
      ind <- c((i - floor(w / 2)):(i + floor(w / 2)))
      ind <- ind[ind %in% seq_len(length(x))]
      y[i] <- mean(x[ind])
    }
    
    y
    

    But it gets fun to make it independent of mean(), so you can calculate any 'moving' function!

    # our working horse:
    moving_fn <- function(x, w, fun, ...) {
      # x = vector with numeric data
      # w = window length
      # fun = function to apply
      # side = side to take, (c)entre, (l)eft or (r)ight
      # ... = parameters passed on to 'fun'
      y <- numeric(length(x))
      for (i in seq_len(length(x))) {
        if (side %in% c("c", "centre", "center")) {
          ind <- c((i - floor(w / 2)):(i + floor(w / 2)))
        } else if (side %in% c("l", "left")) {
          ind <- c((i - floor(w) + 1):i)
        } else if (side %in% c("r", "right")) {
          ind <- c(i:(i + floor(w) - 1))
        } else {
          stop("'side' must be one of 'centre', 'left', 'right'", call. = FALSE)
        }
        ind <- ind[ind %in% seq_len(length(x))]
        y[i] <- fun(x[ind], ...)
      }
      y
    }
    
    # and now any variation you can think of!
    moving_average <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = mean, side = side, na.rm = na.rm)
    }
    
    moving_sum <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = sum, side = side, na.rm = na.rm)
    }
    
    moving_maximum <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = max, side = side, na.rm = na.rm)
    }
    
    moving_median <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = median, side = side, na.rm = na.rm)
    }
    
    moving_Q1 <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = quantile, side = side, na.rm = na.rm, 0.25)
    }
    
    moving_Q3 <- function(x, w = 5, side = "centre", na.rm = FALSE) {
      moving_fn(x = x, w = w, fun = quantile, side = side, na.rm = na.rm, 0.75)
    }
    
    0 讨论(0)
  • 2020-11-22 00:48

    Or you can simply calculate it using filter, here's the function I use:

    ma <- function(x, n = 5){filter(x, rep(1 / n, n), sides = 2)}
    

    If you use dplyr, be careful to specify stats::filter in the function above.

    0 讨论(0)
提交回复
热议问题