Skip to content

Exponential Moving Average (EMA) Filters

Published On:
Feb 26, 2014
Last Updated:
Apr 11, 2024

The exponential moving average (EMA) filter is a discrete, low-pass, infinite-impulse response (IIR) filter. It places more weight on recent data by discounting old data in an exponential fashion, and behaves similarly to the discrete first-order low-pass RC filter.

Unlike a simple moving average (SMA) filter, most EMA filters is not windowed, and the next value depends on all previous inputs. Thus most EMA filters are a form of infinite impulse response (IIR) filter, whilst a SMA is a finite impulse response (FIR) filter. There are exceptions, and you can indeed build a windowed exponential moving average filter in where the coefficients are weighted exponentially.

EMA Equation

The difference equation for an exponential moving average filter is:

y[i]=αx[i]+(1α)y[i1]\begin{align} y[i] = \alpha \cdot x[i] + (1 - \alpha) \cdot y[i-1] \end{align}

where:
yy is the output ([i][i] denotes the sample number)
xx is the input
α\alpha is a constant which sets the cutoff frequency (a value between 00 and 11)

Notice that the calculation does not require the storage of past values of xx and only the previous value of yy, which makes this filter memory and computation friendly (especially relevant for microcontrollers). Only one addition, one subtraction, and two multiplication operations are needed.

The constant α\alpha determines how aggressive the filter is. It can vary between 00 and 11 (inclusive). As α0\alpha \to 0, the filter gets more and more aggressive, until at α=0\alpha = 0, where the input has no effect on the output (if the filter started like this, then the output would stay at 00). As α1\alpha \to 1, the filter lets more of the raw input through at less filtered data, until at α=1\alpha = 1, where the filter is not “filtering” at all (pass-through from input to output).

The filter is called exponential because the weighted contribution of previous inputs decreases exponentially the further the input is away in time. This can be seen in the difference equation if we substitute in previous inputs:

y[i]=αx[i]+(1α)y[i1]y[i]=αx[i]+(1α)(αx[i1]+(1α)y[i2])y[i]=αx[i]+(1α)(αx[i1]+(1α)(αx[i2]+(1α)y[i3]))...y[i]=αk=0n(1α)kx[nk]\begin{align} y[i] &= \alpha \cdot x[i] + (1 - \alpha) \cdot y[i-1] \nonumber \\ y[i] &= \alpha \cdot x[i] + (1 - \alpha) \cdot (\alpha \cdot x[i-1] + (1 - \alpha) \cdot y[i-2]) \nonumber \\ y[i] &= \alpha \cdot x[i] + (1 - \alpha) \cdot (\alpha \cdot x[i-1] + (1 - \alpha) \cdot \nonumber \\ &(\alpha \cdot x[i-2] + (1 - \alpha) \cdot y[i-3])) \nonumber \\ ... \nonumber \\ \label{eq:ema-sum} y[i] &= \alpha \sum_{k=0}^n (1-\alpha)^k x[n-k] \\ \end{align}

The following code implements a basic EMA filter in C++, suitable for microcontrollers and other embedded devices:

#include <stdio.h>
#include <cstdint>
class EmaFilter
{
public:
EmaFilter(double alpha) :
m_alpha(alpha), m_lastOutput(0.0) {}
double Run(double input)
{
m_lastOutput = m_alpha * input + (1 - m_alpha) * m_lastOutput;
return m_lastOutput;
}
private:
double m_alpha;
double m_lastOutput;
};
int main() {
EmaFilter emaFilter(0.5);
for(uint32_t i = 0; i < 20; i++)
{
double input = 1.0;
double output = emaFilter.Run(input);
printf("%f\n", output);
}
}

When setting alpha=0.5 and hardcoding all input to 1.0 (which means a step change from 0.0 to 1.0 at t=0), it prints the following:

0.500000
0.750000
0.875000
0.937500
0.968750
0.984375
0.992188
0.996094
0.998047
0.999023
0.999512
0.999756
0.999878
0.999939
0.999969
0.999985
0.999992
0.999996
0.999998
0.999999

This example can be modified and run at https://replit.com/@gbmhunter/cpp-ema-filter.

Frequency Response

The frequency response of the EMA filter can be found by using the Z transform. If we start with the time-domain equation for an EMA filter:

y[i]=αx[i]+(1α)y[i1]\begin{align} y[i] = \alpha \cdot x[i] + (1 - \alpha) \cdot y[i-1] \end{align}

And then take the Z transform of it:

Y(z)=aX(z)+(1α)z1Y(z)\begin{align} Y(z) = aX(z) + (1 - \alpha) z^{-1} Y(z) \end{align}

Then re-arrange to find the transfer function H(z)H(z):

H(z)=Y(z)X(z)=α1(1α)z1\begin{align} H(z) &= \frac{Y(z)}{X(z)} \nonumber \\ &= \frac{\alpha}{1 - (1-\alpha)z^{-1}} \\ \end{align}

This transfer function can be used to create bode plots of the magnitude and phase response of the EMA filter. The below bode plot shows the response of an EMA filter with α=0.25\alpha=0.25. The x-axis frequency is the normalized frequency, in units Hz/sampleHz/sample, which makes the plot applicable for any sampling frequency.

Bode plot showing the magnitude and phase of an EMA filter with α=0.25\alpha=0.25.

The cut-off frequency (-3dB point) of an EMA filter is given by1:

fc=fs2πcos1[1α22(1α)]\begin{align} f_c = \frac{f_s}{2\pi} \cos^{-1}{\left[1 - \frac{\alpha^2}{2(1-\alpha)}\right]} \end{align}

where:
fsf_s is the sampling frequency in HzHz

An important consequence of this is that the filter’s response is dependent both on α\alpha and the sampling frequency. Remember that if you adjust the sampling frequency, you also have to adjust α\alpha to keep the same response.

Note that not all valid values of α\alpha will result in a valid cut-off frequency. This is when the filter response is so gradual that it does not drop to -3dB or below before the Nyquist frequency. Mathematically, this is when:

1α22(1α)>1\begin{align} \left|1 - \frac{\alpha^2}{2(1 - \alpha)}\right| > 1 \end{align}

and thus the inverse cosine function is undefined. The expression above becomes greater than 1 when α\alpha is at or above1:

α=0.82843\begin{align} \alpha = 0.82843 \end{align}

Impulse Response

The discrete unit sample function is defined as:

δ[n]={1n=00n0\begin{align} \delta[n] = \begin{cases} 1 & n = 0 \\ 0 & n \neq 0 \\ \end{cases} \end{align}

If we use this as our input into Eq. eq:ema-sumEq.\ \ref{eq:ema-sum}:

y[i]=αk=0n(1α)kδ[nk]\begin{align} y[i] &= \alpha \sum_{k=0}^n (1-\alpha)^k \delta[n-k] \\ \end{align}

Given the unit sample function is 0 at most points, the only sum term that matters is when k=nk = n, so we can simplify this to:

y[i]=α(1α)n\begin{align} y[i] &= \alpha (1-\alpha)^n \\ \end{align}

From this, we can plot what the response will look like for impulse as the input. As you can see in the following graph, the output starts off at y[0]=αy[0] = \alpha and then decays towards 0. A larger alpha makes the initial response larger but also the decay faster.

Impulse response for an EMA filter with different α\alpha values.

Initializing (Priming) The Filter

When initializing the filter, the initial value of yy is important. If you set it to 0, then the filter will take a while to “warm up” and reach the correct output. If you set it to the first input value, then the filter will get a “head start”. This may or may not be important for your application. I typically have a boolean flag called firstTime which is set to true when the filter is first created, and then set to false after the first input is processed. On the first pass, the filter just sets the output yy to the input xx. I call this priming the filter, to avoid confusion with “initialisation” of an object (i.e. constructor).

Here is an example of the C++ class from above but reworked to include priming:

class EmaFilterWithPriming
{
public:
EmaFilterWithPriming(double alpha) :
m_alpha(alpha), m_lastOutput(0.0), m_firstRun(true) {}
double Run(double input)
{
if (m_firstRun)
{
m_firstRun = false;
m_lastOutput = input; // Bypass filter and prime with input
}
else
{
m_lastOutput = m_alpha * input + (1 - m_alpha) * m_lastOutput;
}
return m_lastOutput;
}
private:
double m_alpha;
double m_lastOutput;
bool m_firstRun;
};

This example can be modified and run at https://replit.com/@gbmhunter/cpp-ema-filter.

Further Reading

https://stratifylabs.co/embedded%20design%20tips/2013/10/04/Tips-An-Easy-to-Use-Digital-Filter/ is a great page explaining the exponential moving average filter.

References

Footnotes

  1. Andy Walls (2017, Apr 22). Exponential moving average cut-off frequency (Q/A). StackExchange: Signal Processing. Retrieved 2022-05-25, from https://dsp.stackexchange.com/questions/40462/exponential-moving-average-cut-off-frequency. 2