Frequency Characteristics of the Exponential Moving Average Filter

Derivation of the EMA filter transfer function via Z-transform, with gain and phase characteristic formulas and Python visualization code.

Frequency Characteristics of the Exponential Moving Average Filter

Exponential Moving Average (EMA)

The Exponential Moving Average (EMA) is a popular method for smoothing time series data. It computes a new smoothed value by applying a weighted combination of the current observation and the previous smoothed value.

\[ y*t = (1 - \alpha) y*{t-1} + \alpha x_t \]

Where:

  • \(y_t\): EMA value at time \(t\)
  • \(x_t\): Original observation at time \(t\)
  • \(\alpha\): Smoothing coefficient (\(0 < \alpha \le 1\))

This formula places greater weight on recent data while still retaining some information from older observations.

Frequency Characteristics: Gain and Phase

The frequency characteristics of a filter describe how the frequency components of an input signal are altered by the filter. This is analyzed using the filter’s transfer function \(G(s)\).

Applying the Z-transform (used for analyzing discrete-time systems) to the EMA equation, we get the transfer function \(G(z)\):

\[ Y(z) = (1 - \alpha) z^{-1} Y(z) + \alpha X(z) \]

\[ G(z) = \frac{Y(z)}{X(z)} = \frac{\alpha}{1 - (1 - \alpha) z^{-1}} \]

By substituting \(z = e^{j\omega T}\) (where \(T\) is the sampling period, assumed to be \(T=1\) here), we obtain the frequency response \(G(j\omega)\):

\[ G(j\omega) = \frac{\alpha}{1 - (1 - \alpha) e^{-j\omega}} \]

From this frequency response, we can derive the gain characteristic \(|G(j\omega)|\) and the phase characteristic \(\angle G(j\omega)\).

Gain Characteristic

\[ |G(j\omega)| = \frac{\alpha}{\sqrt{1 - 2(1 - \alpha) \cos \omega + (1 - \alpha)^2}} \]

Gain Characteristic

Phase Characteristic

\[ \angle G(j\omega) = \arctan\left(\frac{(1 - \alpha) \sin \omega}{1 - (1 - \alpha) \cos \omega}\right) \]

Phase Characteristic

Discussion:

  • Small \(\alpha\) (e.g., \(\alpha=0.1\)): The filter’s frequency bandwidth becomes narrower, resulting in a stronger smoothing effect. However, this also leads to a larger phase lag.
  • Large \(\alpha\) (e.g., \(\alpha=0.9\)): The smoothing capability decreases, but the phase lag is smaller.

This demonstrates a trade-off between the degree of smoothing and the response speed (phase lag).

Program

The following Python code computes and plots the gain and phase characteristics of the EMA filter.

import math
import numpy as np
import matplotlib.pyplot as plt

# List of smoothing coefficient alpha values
ALPHAS = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
# Frequency range (rad/s)
FREQ_MAX = 300
NUM_POINTS = 500

def get_gain(alpha, omega):
    """Compute the gain of the EMA filter"""
    denominator = np.sqrt(1 - 2 * (1 - alpha) * np.cos(omega) + (1 - alpha)**2)
    return alpha / denominator

def get_phase(alpha, omega):
    """Compute the phase of the EMA filter (radians)"""
    numerator = (1 - alpha) * np.sin(omega)
    denominator = 1 - (1 - alpha) * np.cos(omega)
    return np.arctan2(numerator, denominator)

# Data storage lists for plotting
frequencies = [[] for _ in range(len(ALPHAS))]
gains = [[] for _ in range(len(ALPHAS))]
phases = [[] for _ in range(len(ALPHAS))]

# Compute for each alpha value
for alpha_idx, alpha_val in enumerate(ALPHAS):
    for i in range(NUM_POINTS):
        omega = i * (np.pi / NUM_POINTS)
        frequencies[alpha_idx].append(omega)
        gains[alpha_idx].append(get_gain(alpha_val, omega))
        phases[alpha_idx].append(np.degrees(get_phase(alpha_val, omega)))

# Plot gain characteristics
plt.figure(figsize=(10, 6))
plt.xlabel('Frequency (rad/s)')
plt.ylabel('Gain')
plt.title('EMA Filter Gain Characteristics')
for alpha_idx, alpha_val in enumerate(ALPHAS):
    plt.plot(frequencies[alpha_idx], gains[alpha_idx], label=f"alpha={alpha_val}")
plt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
plt.grid(True)
plt.tight_layout()
plt.show()

# Plot phase characteristics
plt.figure(figsize=(10, 6))
plt.xlabel('Frequency (rad/s)')
plt.ylabel('Phase (degrees)')
plt.title('EMA Filter Phase Characteristics')
for alpha_idx, alpha_val in enumerate(ALPHAS):
    plt.plot(frequencies[alpha_idx], phases[alpha_idx], label=f"alpha={alpha_val}")
plt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
plt.grid(True)
plt.tight_layout()
plt.show()

Comparison of EMA with Other Filters

The EMA filter is simple to implement and has low computational cost, but it has different characteristics compared to other filters.

FilterCutoff CharacteristicsComputational CostParametersReal-time Capability
EMAGentle\(O(1)\)1 (\(\alpha\))Excellent
Moving AverageGentle\(O(N)\)1 (window size \(N\))Excellent
ButterworthSharp (order-dependent)\(O(N)\)2 (order, cutoff frequency)Good
FIR/IIRDesign-dependent\(O(N)\)MultipleFIR: Good, IIR: Excellent

The EMA can be viewed as a first-order IIR filter, and the fact that it is controlled by a single parameter \(\alpha\) is a significant practical advantage. For sharper cutoff characteristics, consider a Butterworth filter; to minimize phase lag, consider a Wiener filter.

EMA Implementation Using scipy.signal

Python’s scipy.signal module allows you to implement EMA as an IIR filter.

import numpy as np
from scipy import signal

def ema_scipy(x, alpha):
    """EMA implementation using scipy.signal"""
    # EMA: y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
    # IIR transfer function: H(z) = alpha / (1 - (1-alpha) * z^{-1})
    b = [alpha]           # Numerator coefficients
    a = [1, -(1 - alpha)] # Denominator coefficients
    return signal.lfilter(b, a, x)

# Usage example
np.random.seed(42)
t = np.linspace(0, 1, 1000)
x = np.sin(2 * np.pi * 5 * t) + 0.5 * np.random.randn(len(t))
y = ema_scipy(x, alpha=0.1)

This implementation is faster than a manual loop and provides a unified interface with other scipy.signal filter functions (butter, firwin, etc.).

References