Moving average (and rolling mean) using `stats::filter`

Viewed 69

I am learning to calculate moving averages in R with this code:

x <- rnorm(n = 100, mean = 0, sd = 10)

mn <- function(n) rep (1 / n, n)

y <- filter(x, mn(5)) 

When I plot only mn(5), I see that 1/5 is repeated 5 times. My questions are:

  1. Why using filter(x, mn(5)) calculates the average of the five values?

  2. What part of x is averaged?

2 Answers

1) The mean is the average of the values so assuming x has 5 elements we can write the second line and that is the same as the third line and the fourth line so using coefficients of (1/5, 1/5, 1/5, 1/5, 1/5) in the sum is equivalent to taking the mean.

mean(x) 
= (x[1] + x[2] + x[3] + x[4] + x[5])/5
= x[1]/5 + x[2]/5 + x[3]/5 + x[4]/5 + x[5]/5
= sum(x * c(1/5, 1/5, 1/5, 1/5, 1/5))

2) Another way to understand this is to note that mean is linear. That is if x and y are two vectors of the same length then mean(x+y) = mean(x) + mean(y) and if a is any scalar then mean(a * x) = a * mean(x). Now it is known that any linear function that returns a scalar is representable as the inner product of some vector times the input. That is there is a vector v such that

mean(x)
sum(v * x)

are equal for all x. Now since it is true for all x it must be true for x <- c(1, 0, 0, 0, 0) so these are equal

mean(c(1, 0, 0, 0, 0)
v[1] * x[1]

but the second line equals v[1] since x[1] is 1 and the mean of c(1, 0, 0, 0, 0) in the first line equals 1/5 and similarly for

mean(c(0, 1, 0, 0, 0))
v[2] * x[2]

etc. so v must equal c(1/5, 1/5, 1/5, 1/5, 1/5).

What part of x is averaged?

See ?filter for argument sides. The default value sides = 2 means "center". You probably want sides = 1 to use past values only.

In general,

y <- filter(x, c(w1, w2, w3, w4, w5), sides = 1)

computes:

y[1:4] = NA
y[5] = w1 * x[5] + w2 * x[4] + w3 * x[3] + w4 * x[2] + w5 * x[1]
y[6] = w1 * x[6] + w2 * x[5] + w3 * x[4] + w4 * x[3] + w5 * x[2]
## and etc.

Why using filter(x, mn(5)) calculates the average of the five values?

In this particular case:

y <- filter(x, c(1/5, 1/5, 1/5, 1/5, 1/5), sides = 1)

computes:

y[1:4] = NA
y[5] = (x[5] + x[4] + x[3] + x[2] + x[1]) / 5 = mean(x[5:1])
y[6] = (x[6] + x[5] + x[4] + x[3] + x[2]) / 5 = mean(x[6:2])
## and etc.

which gives rolling mean.


Remark:

Moving average is much more general than rolling mean. Another way to compute simple rolling mean is

zoo::rollmean(x, k = 5, na.pad = TRUE, align = "right")

There is also an R package called RcppRoll.

Related