A simple moving average (SMA: arithmetic mean) is a low-pass FIR-filter. When you cascade 2 SMA's with a window of length $n$, so when you apply the second SMA on the output of the first SMA, and you want to know which coefficient weights a filter would have with the same output result, applied to the original signal, the 2 impulse responses (coefficients/weights) need to undergo a convolution (non-circular): this should return a vector of $(2*n)-1$ coefficients / weights. I refer to the image below: you have a sinusoidal signal (black), an SMA (blue), and an SMA of this SMA (green), and I compared this to a filter with the "convolution weights":
If you do this in R: the command should be convolve(rep(1/n,n),rep(1/n,n),conj = FALSE,type="open")
, right? Obviously the red curve does not coincide with the green curve. Is there an analytic solution that returns the weight vector for this new moving average? Not just for 1 SMA on 1 SMA, but for $x$ SMA's on $x$ SMA's?
Here's my pretty basic R code (I assume Matlab-users / dsp-engineers understand):
n<-10
vperiod<-40
vwave<-sin(2*pi*1/vperiod*(1:(600)))
#
SMA<-function(x,n)
{
out<-c()
for (i in n:length(x))
{
out[i]<-(sum((x[(i-(n-1)):i])*rep((1/n),n)))
}
out[1:(n-1)]<-out[n]
return(out)
}
#
SmaOnSma<-function(x,n)
{
out<-c()
cnvweights<-convolve(rep(1/n,n),rep(1/n,n),conj = FALSE,type="open")
for (i in length(cnvweights):length(x))
{
out[i]<-sum(x[(i-(length(cnvweights))+1):i]*cnvweights)
}
out[1:(n-1)]<-out[n]
return(out)
}
#
plot(vwave[(2*vperiod):(3*vperiod)],type="l",lwd=2,main="SMA on SMA vs. convolution weights");abline(h=0,lty=3,col="gray")
legend("bottomleft",inset=.03,c("signal","SMA of signal","SMA of SMA of signal","Filter with convolution weights"),fill=c("black","blue","green","red"),horiz=FALSE,border="white",box.col="white")
#
tempwave<-vwave
lines(SMA(tempwave,n)[(2*vperiod):(3*vperiod)],col="blue",lwd=2)
tempwave<-SMA(tempwave,n)
lines(SMA(tempwave,n)[(2*vperiod):(3*vperiod)],col="green",lwd=2)
lines(SmaOnSma(vwave,n)[(2*vperiod):(3*vperiod)],col="red",lwd=2)
Update:
Thanks to the kind answers here is the code in R for a moving average on moving average:
smavector<-function(n)
{
return(rep((1/n),n))
}
SmaOnSma<-function(x,n,nit)
{
if (nit==1)
{
cnvweights<-smavector(n)
}
if (nit==2)
{
cnvweights<-convolve(smavector(n),smavector(n),conj = TRUE,type="open")
}
if (nit>2)
{
cnvweights<-convolve(smavector(n),smavector(n),conj = TRUE,type="open")
for (j in 1:(nit-2))
{
cnvweights<-convolve(smavector(n),cnvweights,conj = TRUE,type="open")
}
}
#
out<-c()
for (i in length(cnvweights):length(x))
{
out[i]<-sum(x[(i-(length(cnvweights))+1):i]*cnvweights)
}
out[1:(n-1)]<-out[n]
return(out)
}
I initially thought this could be a method for estimating the instantaneous frequency of a smooth curve, by correcting for the SMA's frequency response, as per my previous question on SE: which was calculated as ($(sin(n*(\pi/p)))/(n*sin(\pi/p))$), (with p = the period of the wave = 1/frequency), but it becomes clear that with every iteration, the length of the weight vector grows by a factor of $(2*n)+1$ the previous weight vector length. Even if you do only half the iterations and multiply with $-1$, the minimum length of required input data (with the same frequency), is still $2$ times the period + $1$.