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.

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()

References