1
$\begingroup$

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":

SMA on SMA vs. 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$.

Frequency-response-corrected SMA on SMA as Ifreq estimate

$\endgroup$

2 Answers 2

3
$\begingroup$

The green curve in your plot corresponds to the correct result. The red curve is wrong, because the weights in the vector cnvweights are in the wrong order (left and right halves are interchanged). The correct way to compute those weights is

cnvweights <- convolve(rep(1/n,n), rev(rep(1/n,n)), conj = TRUE, type = "open")

According to the R documentation of the function 'convolve', you need to use conj = TRUE for standard convolution.

$\endgroup$
3
  • $\begingroup$ yes, of course, thanks; as I wrote below: the Sma on Sma triangular coefficient weights can also be calculated like this: c(1:n*(1/n^2),rev(1:n*(1/n^2))[2:n]) $\endgroup$
    – MisterH
    Aug 3, 2020 at 11:41
  • $\begingroup$ I see that if you repeat the convolution, it results in multi-pass MA's, where the number of passes is a power of 2. 1 convolution = SMaOfSma, 2 convolutions: SMaOfSmaOfSmaOfSma. 3 convulations= 8 iterations. Would there be a direct way to calculate the weights for a number of iterations that is not a power of 2? e.g. SmaOfSmaOfSma? $\endgroup$
    – MisterH
    Aug 3, 2020 at 12:01
  • $\begingroup$ @MisterH: Just repeat convolution with one sequence always being rectangular. $\endgroup$
    – Matt L.
    Aug 3, 2020 at 12:03
1
$\begingroup$

Here’s are pretty good source on the subject: http://www.dspguide.com/ch15/4.htm

A single moving average is a boxcar. Two is a triangle. Beyond that, it will start to approach a Gaussian.

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.