Part II: Classical Signal Processing and Feature Engineering
Chapter 8: Feature Engineering and Dimensionality Reduction

Frequency- and time-frequency features

"A spectrogram is a thousand pixels. A classifier wants a dozen numbers. My job is to decide which dozen carry the meaning and which nine hundred are scenery."

A Discerning AI Agent

The Big Picture

Chapter 7 gave you the representations: the power spectrum, the spectrogram, the wavelet decomposition. Those are pictures, and a picture is hundreds or thousands of numbers. A classical model wants a short, fixed-length vector, the same length for a walking window and a running window, for a healthy motor and a failing one. This section is the bridge. It collapses a spectrum or a time-frequency surface into a handful of scalars that each answer a physical question: how much energy, at what frequency, spread how widely, how tonal versus how noisy, and how fast the picture is changing. These are the workhorses of vibration diagnostics, audio event detection, and biosignal staging, and they are what a small classifier consumes when it beats a neural network on limited data (Section 8.7).

This section assumes you can compute a power spectrum and a short-time Fourier transform from Chapter 7, and that you understand the sampling rate and Nyquist limit from Chapter 3, since every frequency-axis feature is meaningless without the correct \(f_s\). Where we touch entropy, we compute it on the spectrum; the general entropy toolkit lives in Section 8.3. The raw-waveform complement was Section 8.1; here every feature is read off a transform.

Band powers: the first and most physical features

The simplest frequency feature answers "how much energy lives between two frequencies?" Split the spectrum into bands chosen from the physics of your sensor, then integrate the power spectral density \(S(f)\) over each band:

$$P_b = \sum_{f_k \in [f_\text{lo}^{(b)},\, f_\text{hi}^{(b)}]} S(f_k).$$

The bands are not arbitrary. For EEG staging you use the canonical delta, theta, alpha, beta, and gamma bands neurophysiology defined; for a rolling-element bearing you place bands around the shaft rate and its harmonics; for accelerometer activity recognition you separate the sub-1 Hz gravity and gait band from the higher tremor band. Absolute band powers drift with sensor gain and contact quality, so the feature that generalizes is usually a ratio: \(P_b / \sum_j P_j\) (the fraction of total energy in band \(b\)) or a pairwise ratio like alpha-over-theta. Ratios cancel the unknown multiplicative gain, exactly the nuisance that wrecks a classifier trained on one device and tested on another.

Key Insight: Features Inherit the Spectrum's Resolution and Its Lies

Every frequency feature is computed on an estimated spectrum, and that estimate has a resolution \(\Delta f \approx f_s / N\) and a variance set by how many segments you averaged (Welch's method). If your band is 2 Hz wide but \(\Delta f\) is 5 Hz, the band contains one bin and the "band power" is noise. If you skipped the anti-alias filter from Chapter 6, energy from above Nyquist has folded into your bands and your feature is measuring an artifact with full confidence. Spectral leakage from a rectangular window smears a sharp tone across neighbors and inflates the centroid. The lesson: a frequency feature is only as honest as the spectrum under it, so match band widths to \(\Delta f\), window before transforming, and average enough segments that the feature is repeatable on two halves of the same recording.

Spectral shape descriptors

Band powers say where the energy sits; a second family describes the shape of the spectrum as a whole, treating the normalized power spectrum as a probability distribution over frequency, \(p_k = S_k / \sum_j S_j\). The spectral centroid is its mean, the "center of mass" of the spectrum and a good proxy for perceived brightness or for a machine's dominant running frequency:

$$c = \sum_k f_k\, p_k, \qquad \text{spread} = \sqrt{\sum_k (f_k - c)^2\, p_k}.$$

The spread (bandwidth) is the standard deviation around the centroid; higher moments give spectral skewness and kurtosis, flagging whether energy is lopsided or peaky. Three more descriptors carry outsized weight. Spectral rolloff is the frequency below which a fixed fraction (commonly 85% or 95%) of the energy lies, a robust summary of the high-frequency edge. Spectral flatness, the geometric mean over the arithmetic mean of the power spectrum, runs from near 0 for a pure tone to near 1 for white noise, distinguishing a tonal fault whine from broadband turbulence in one number. Spectral entropy, \(H = -\sum_k p_k \log p_k\), measures how spread out the energy is: a dominant peak gives low entropy, a flat spectrum high. Together these turn a whole spectrum into six to eight scalars a tree ensemble reads instantly.

Practical Example: Diagnosing a Failing Bearing in a Conveyor Motor

A predictive-maintenance team instrumented conveyor drive motors with 20 kHz accelerometers (Chapter 36). A healthy motor's spectrum is dominated by the shaft-rate line and its low harmonics: its energy fraction below 2 kHz is high, its flatness is low, its centroid sits near the running frequency. As an outer-race defect develops, each ball passing the defect rings the bearing's structural resonance in the 3 to 8 kHz band. Long before a human hears anything, three features move in concert: the high-band energy ratio climbs, the centroid drifts upward, and flatness falls as the defect ring becomes a sharp modulated line. Feeding those three plus two band-power ratios to a gradient-boosted tree, the team flagged degrading bearings a week ahead of failure, on a model small enough for the edge gateway. No spectrogram pixels left the device; only the feature vector did. The same descriptors reappear in condition monitoring in Chapter 37.

Time-frequency features: summarizing the whole surface

A spectrum is one frame; a spectrogram is many frames stacked over time, and non-stationary signals (a gesture, a cough, a gait cycle) live in that time-frequency surface. Its width changes with window length, so you cannot feed the whole surface as a fixed vector: you summarize. Two moves dominate. First, aggregate each spectral feature over time: compute the centroid, band powers, and entropy per frame, then take the mean, standard deviation, and slope across frames. The standard deviation captures how much the spectrum wobbles, and spectral flux, the frame-to-frame change \(\sum_k (S_k^{(m)} - S_k^{(m-1)})^2\), measures how fast the picture turns over, separating a sustained tone from a rattle. Second, use energies from a wavelet packet or filterbank decomposition: the energy in each subband over the window is a compact, multiresolution vector, sharper in time up high and sharper in frequency down low. The MFCC (mel-frequency cepstral coefficient) vector specializes this for audio: it warps frequency to the mel scale, log-compresses the band energies, and takes a discrete cosine transform to decorrelate them into ten to thirteen coefficients that summarize spectral shape with remarkable economy.

import numpy as np
from scipy.signal import welch

def spectral_features(x, fs, bands):
    f, S = welch(x, fs=fs, nperseg=1024)      # averaged PSD, honest variance
    S = S + 1e-12                              # guard the logs and ratios
    p = S / S.sum()                           # spectrum as a distribution
    centroid = np.sum(f * p)
    spread   = np.sqrt(np.sum((f - centroid) ** 2 * p))
    flatness = np.exp(np.mean(np.log(S))) / np.mean(S)   # 0=tonal, 1=noise
    entropy  = -np.sum(p * np.log(p))
    rolloff  = f[np.searchsorted(np.cumsum(p), 0.85)]    # 85% energy edge
    total    = S.sum()
    ratios   = [S[(f >= lo) & (f < hi)].sum() / total for lo, hi in bands]
    return dict(centroid=centroid, spread=spread, flatness=flatness,
                entropy=entropy, rolloff=rolloff, band_ratios=ratios)

fs = 20000
t = np.arange(0, 1.0, 1 / fs)
healthy = np.sin(2*np.pi*30*t) + 0.05*np.random.randn(t.size)
faulty  = healthy + 0.4*np.sin(2*np.pi*5200*t)*(1 + np.sin(2*np.pi*30*t))
bands = [(0, 2000), (2000, 5000), (5000, 9000)]
print("healthy:", spectral_features(healthy, fs, bands)["band_ratios"])
print("faulty :", spectral_features(faulty,  fs, bands)["band_ratios"])
A reusable spectral-feature extractor built on Welch's averaged PSD. Running it on the synthetic healthy versus faulty motor signals shows the high-band energy ratio jumping and the centroid rising once the 5.2 kHz modulated defect line appears, reproducing the bearing story above in a dozen lines.

The function above is the from-scratch version worth reading once: note the \(10^{-12}\) floor that keeps the geometric mean and log-entropy finite, and the Welch averaging rather than a single raw FFT so the features stay repeatable across two halves of a recording.

Right Tool: librosa Owns the Spectral Feature Zoo

Hand-rolling centroid, bandwidth, rolloff, flatness, flux, contrast, and the full MFCC pipeline with correct framing, windowing, mel filterbank, and safe logs is roughly 120 to 150 lines. With librosa the per-frame surface plus its aggregates is a handful:

import librosa, numpy as np
S = np.abs(librosa.stft(faulty, n_fft=1024, hop_length=256))
feats = np.concatenate([
    librosa.feature.spectral_centroid(S=S, sr=fs),
    librosa.feature.spectral_bandwidth(S=S, sr=fs),
    librosa.feature.spectral_rolloff(S=S, sr=fs),
    librosa.feature.spectral_flatness(S=S),
    librosa.feature.mfcc(S=librosa.power_to_db(S**2), n_mfcc=13),
])
vector = np.concatenate([feats.mean(axis=1), feats.std(axis=1)])  # fixed length

Roughly 120 to 150 lines of framing, filterbank, and cepstral bookkeeping collapse to about eight, and the output is already a fixed-length vector no matter how long the window. The library owns the mechanics; you still own the one thing it cannot guess, which bands and which window length match your phenomenon.

A fixed-length time-frequency feature vector with librosa, aggregating per-frame descriptors and MFCCs by mean and standard deviation across frames.

Making frequency features generalize

Three habits keep these features from betraying you at deployment. First, normalize away nuisance gain: prefer energy ratios and log-scaled magnitudes over raw powers, since sensor sensitivity and coupling vary unit to unit. Second, match window and band widths to the event, as the Key Insight warned; a feature at the wrong resolution is not weak, it is wrong. Third, respect evaluation hygiene from Chapter 5: fit any per-feature scaler on the training split only, and never let windows from one recording straddle the train and test boundary, or the spectrum's slow drift leaks the label. Get these right and a six-to-twenty-dimensional frequency vector is often all a strong classical model needs, and it stays legible: you can point at "high-band energy ratio" and explain the alarm to an engineer, which the raw spectrogram tensor fed to a network in Chapter 13 cannot do.

Exercise

Using the spectral_features function, (1) sweep the modulation carrier of the faulty signal from 3 kHz to 8 kHz and plot how the centroid, flatness, and high-band ratio each respond; (2) reduce nperseg from 1024 to 128 and explain, with \(\Delta f = f_s/N\), why the flatness and band ratios become noisier; (3) add a per-recording random gain of \(0.5\times\) to \(2\times\) and confirm that the band ratios stay stable while absolute band powers do not. State which features you would ship across devices and why.

Self-Check

  1. Spectral flatness near 0.9 versus near 0.05: what does each say about whether a signal is a tone or broadband noise, and why is the geometric-over-arithmetic-mean ratio the right construction?
  2. Why does a band-power ratio generalize across devices better than the absolute band power, and what nuisance does the ratio cancel?
  3. Your defect band is 3 Hz wide but your spectrum's \(\Delta f\) is 10 Hz. What is wrong with the resulting band-power feature, and what two changes fix it?

What's Next

In Section 8.3, we broaden from the frequency domain to statistical, shape, and entropy features computed directly on the signal and its distribution: moments, quantiles, autocorrelation structure, and complexity measures like sample and permutation entropy. Together with the time-domain features of Section 8.1 and the frequency features here, they complete the classical feature palette that the domain libraries of Section 8.4 then compute by the hundreds.