Part II: Classical Signal Processing and Feature Engineering
Chapter 7: Spectral and Time-Frequency Analysis

Wavelets and multiresolution analysis

"A wavelet is a wave with the good manners to stop. It shows up, does its measuring, and leaves before it blurs everything else."

A Multiresolution AI Agent

The Big Picture

The spectrogram of Section 7.2 commits to one window length and therefore one time-frequency resolution for the entire signal. That is fine when your events all live at similar timescales, and painful when they do not: a slow drift and a sharp click in the same accelerometer trace want opposite windows. Wavelets dissolve the compromise. Instead of a single fixed window, they use a family of scaled copies of one little wave, short ones to pin down fast transients and long ones to resolve slow oscillations, all at once. The payoff is a representation that is sharp in time where the signal is fast and sharp in frequency where the signal is slow. This section shows how that family is built, how the fast version runs in linear time, and why it has become the default tool for transients, edges, and machine-fault vibration.

This section assumes you are comfortable with sampling and the frequency response idea from Chapter 3, and that you have met the short-time Fourier transform in Section 7.2. We build on both: the wavelet transform is best understood as the STFT's answer to its own worst weakness.

Why a fixed window is the wrong idea

The STFT slides a window of fixed length \(L\) across the signal and takes a Fourier transform inside each position. That single choice of \(L\) sets your resolution everywhere. A short window localizes events in time but smears them in frequency; a long window resolves close frequencies but cannot say when anything happened. The uncertainty principle makes this a hard tradeoff, not a tuning oversight: the product of time spread and frequency spread has a floor. With the STFT you buy one point on that tradeoff curve and live with it across the whole recording.

Real sensor signals rarely cooperate. A bearing vibration carries a low-frequency shaft rotation you want resolved finely in frequency, alongside sharp impact bursts you want pinned finely in time. Pick a long window and the impacts blur into a haze; pick a short window and the shaft harmonics merge. Wavelets answer with a different geometry: make the analysis window shrink as frequency rises. High-frequency content is inspected with short windows (good timing), low-frequency content with long windows (good frequency precision). This is constant-Q analysis: each band keeps a fixed ratio of center frequency to bandwidth, which matches how many natural and mechanical systems actually behave.

Key Insight

The STFT tiles the time-frequency plane with identical rectangles. The wavelet transform tiles it with rectangles that are tall and thin at high frequency (precise timing) and short and wide at low frequency (precise pitch). Same total area per tile, the uncertainty floor is never beaten, but the shape is allocated where the signal needs it instead of fixed in advance.

The continuous wavelet transform

Start from one prototype function \(\psi(t)\), the mother wavelet: a short, zero-mean, finite-energy wiggle. From it, generate a whole family by scaling and shifting,

$$\psi_{a,b}(t) = \frac{1}{\sqrt{a}}\,\psi\!\left(\frac{t-b}{a}\right),$$

where \(b\) slides the wavelet along time and \(a>0\) stretches it. Small \(a\) squeezes the wavelet into a brief, high-frequency probe; large \(a\) dilates it into a long, low-frequency one. The \(1/\sqrt{a}\) keeps the energy constant across scales. The continuous wavelet transform (CWT) is then just the correlation of the signal with every member of this family,

$$W(a,b) = \int_{-\infty}^{\infty} x(t)\,\psi_{a,b}^{*}(t)\,dt.$$

Each coefficient \(W(a,b)\) answers one question: how much does the signal, near time \(b\), look like the wavelet stretched to scale \(a\)? Plot \(|W(a,b)|^2\) over time and scale and you get a scalogram, the wavelet cousin of the spectrogram. Scale maps inversely to frequency: a chosen wavelet has a center frequency \(f_c\), and scale \(a\) corresponds roughly to \(f \approx f_c f_s / a\). The zero-mean requirement is not decorative; it is the admissibility condition that guarantees the transform is invertible, so no information is silently lost. A popular choice for sensor work is the complex Morlet wavelet, a Gaussian-windowed sinusoid, because it gives smooth magnitude and usable phase in one shot.

Multiresolution analysis and the fast DWT

The CWT computes coefficients at every scale and every shift, which is redundant and slow. The discrete wavelet transform (DWT) keeps only a dyadic grid: scales double at each step (\(a = 1, 2, 4, 8, \dots\)) and shifts step in proportion. This is where multiresolution analysis (MRA) turns an integral into an algorithm. The insight, due to Mallat, is that one step of the DWT is exactly a pair of filters from Chapter 6 followed by downsampling.

At each level you split the signal into two halves of the spectrum. A low-pass filter produces the approximation coefficients (the coarse, smoothed version); a high-pass filter produces the detail coefficients (the fine structure lost in that smoothing). Because each output now spans half the band, you can discard every second sample without loss, then feed the approximation back into the same filter pair to split again. Cascading this gives a filter-bank tree: level 1 details capture the finest timescale, level 2 the next coarser, and so on, with the surviving approximation holding the slowest trend. The filters are a matched quadrature mirror pair; the low-pass one is the scaling function, the high-pass one the wavelet. Because each level works on half as many samples as the last, the whole decomposition costs \(O(N)\), cheaper than the FFT's \(O(N\log N)\), and it is perfectly invertible: run the filters in reverse to reconstruct \(x[n]\) exactly.

import numpy as np
import pywt

fs = 12000.0                       # bearing vibration, 12 kHz
x  = np.load("bearing_vibration.npy")

# 5-level multiresolution decomposition with a Daubechies-4 wavelet
coeffs = pywt.wavedec(x, wavelet="db4", level=5)
cA5, cD5, cD4, cD3, cD2, cD1 = coeffs   # 1 approximation, 5 detail bands

# Energy per band: where does the fault signature live?
for name, c in zip(["A5", "D5", "D4", "D3", "D2", "D1"], coeffs):
    print(f"{name}: energy = {np.sum(c**2):10.1f}, n = {len(c)}")

# Perfect reconstruction from the same coefficients
x_rec = pywt.waverec(coeffs, wavelet="db4")
print("max reconstruction error:", np.max(np.abs(x_rec[:len(x)] - x)))
A 5-level DWT of a 12 kHz bearing-vibration record. wavedec returns one approximation band (A5) and five detail bands (D1 highest frequency to D5 lowest). The per-band energy loop localizes the impact energy to a specific detail level; waverec confirms the decomposition is loss-free to floating-point precision.

The code above is the workhorse pattern: decompose, inspect where energy concentrates across bands, then either build features from the band energies or reconstruct after modifying coefficients. The detail-band energies alone are compact, discriminative features that feed directly into the classifiers of Chapter 8.

Right Tool: PyWavelets hides the filter bank

Hand-coding a five-level DWT means writing the convolution, the correct boundary handling (symmetric, periodic, or zero padding all give different edge artifacts), the decimation, and the mirror-image reconstruction, easily 60 to 80 lines that are painful to validate for exact invertibility. pywt.wavedec and pywt.waverec collapse the whole cascade to two calls and ship dozens of validated wavelet families. You choose the wavelet name and the depth; the library owns the numerics and the edge modes.

Choosing a wavelet and denoising with it

The mother wavelet is a modeling choice, not a universal constant. Haar (a single up-down step) is the simplest and reacts crisply to sharp discontinuities but represents smooth signals poorly. The Daubechies family (db2, db4, ...) trades a longer support for more vanishing moments, meaning it ignores polynomial trends and responds only to genuine structure, which makes it a strong default for vibration and biosignals. Symlets are near-symmetric Daubechies variants that reduce phase distortion; Coiflets add vanishing moments on the scaling side. The practical rule: pick a wavelet whose shape resembles the feature you care about. Match a wavelet to a bearing impact and its coefficients light up exactly where the impact is.

That shape-matching is what makes wavelets superb at denoising. Real transients concentrate their energy into a few large coefficients, while broadband noise spreads thinly across all of them. So you transform, threshold the small coefficients toward zero (soft thresholding shrinks, hard thresholding zeroes), and reconstruct. The signal survives; the noise, having no compact wavelet representation, is mostly discarded. This wavelet shrinkage is the nonlinear counterpart to the linear filters of Chapter 6, and it preserves sharp edges that a low-pass filter would round off.

Practical Example: catching a bearing fault three weeks early

A wind-turbine operator streamed 12 kHz accelerometer data from a gearbox bearing. The spectrogram showed nothing alarming: the fault's impact energy was buried under the dominant gear-mesh tone, and any window long enough to resolve the mesh frequency smeared the impacts into the background. A five-level Daubechies decomposition split the record so that the shaft and mesh harmonics fell into the coarse approximation while the periodic bearing impacts landed almost entirely in the D2 detail band. Tracking the D2 energy over days turned a flat, noisy trend into a clean rising ramp that crossed an alert line three weeks before the vibration RMS did. The maintenance team swapped the bearing on a planned stop instead of a failure. This is exactly the workflow that Chapter 37 builds into a monitoring system.

When to reach for wavelets

Choose wavelets when your signal mixes timescales, when transients and edges carry the information, or when you want a compact, sparse representation for denoising or compression. They shine on non-stationary vibration, ECG and EEG morphology, and any signal where when and how sharp matter as much as how fast. Stay with the plain FFT of Section 7.1 when the signal is stationary and you only need its steady spectrum, and with the STFT of Section 7.2 when a single, human-readable time-frequency resolution is enough. Wavelets are not free: the scale-to-frequency mapping is approximate, the choice of mother wavelet is a genuine design decision, and the dyadic frequency grid is coarse. But when the fixed window fails you, this is the first tool to reach for.

Exercise

Take a signal that is a 50 Hz sine for its first half and a single sharp impulse in its second half, sampled at 1 kHz, with white noise added. (1) Compute a spectrogram and a scalogram (use pywt.cwt with a Morlet wavelet) and compare how each localizes the impulse in time. (2) Run a 5-level db4 DWT and report which detail band holds most of the impulse energy versus the sine energy. (3) Soft-threshold the detail coefficients, reconstruct, and measure the signal-to-noise improvement against a plain low-pass filter tuned to the same job.

Self-Check

  1. In one sentence, what does the wavelet transform do differently from the STFT in the time-frequency plane, and why does that not violate the uncertainty principle?
  2. One level of the DWT is which two Chapter 6 operations, applied in what order?
  3. Why does wavelet thresholding remove broadband noise while preserving a sharp transient, when a low-pass filter cannot do both?

What's Next

In Section 7.4, we push further into fully data-driven decompositions: empirical mode decomposition and the Hilbert-Huang transform, which build their own basis functions from the signal itself instead of choosing a wavelet in advance, and pay for that flexibility with a very different set of tradeoffs.