A single Fourier transform of an entire signal tells you which frequencies are present overall, but loses all information about when each frequency occurred — spectrogram() addresses this by splitting the signal into short, overlapping time segments, windows, and computing a separate frequency spectrum for each one, returning arrays of frequencies, time segments, and the resulting power at each frequency-time combination. This time-frequency view is standard for analyzing signals whose frequency content genuinely changes over time, like speech, music, or many real-world sensor recordings, where a single overall spectrum would hide that time-varying structure.
1Understanding signal.spectrogram()
A single Fourier transform of an entire signal tells you which frequencies are present overall, but loses all information about when each frequency occurred — spectrogram() addresses this by splitting the signal into short, overlapping time segments, windows, and computing a separate frequency spectrum for each one, returning arrays of frequencies, time segments, and the resulting power at each frequency-time combination. This time-frequency view is standard for analyzing signals whose frequency content genuinely changes over time, like speech, music, or many real-world sensor recordings, where a single overall spectrum would hide that time-varying structure.
The nperseg parameter controls the tradeoff between time resolution and frequency resolution — a shorter segment gives better time resolution, seeing frequency changes more precisely in time, but worse frequency resolution, and vice versa for a longer segment; there's no way to have both simultaneously to unlimited precision.
from scipy import signal
import numpy as np
fs = 1000
t = np.linspace(0, 1, fs, endpoint=False)
x = np.sin(2 * np.pi * 50 * t)
frequencies, times, Sxx = signal.spectrogram(x, fs, nperseg=1000)
print(Sxx.shape)2Practical Example
Here is a real-world application of signal.spectrogram() showing how it is used in production SciPy code.
from scipy import signal
import numpy as np
fs = 1000
t = np.linspace(0, 1, fs, endpoint=False)
x = np.sin(2 * np.pi * 50 * t)
frequencies, times, Sxx = signal.spectrogram(x, fs, nperseg=1000)
dominant_freq_index = np.argmax(Sxx[:, 0])
print(round(frequencies[dominant_freq_index]))3Best Practices
Follow these guidelines when working with signal.spectrogram():
1. Use spectrogram() instead of a single full-signal FFT whenever the frequency content is expected to change meaningfully over the duration of the signal
2. Adjust nperseg deliberately based on whether time resolution or frequency resolution matters more for your specific analysis, since improving one comes at the cost of the other
3. Set fs, the sampling frequency, accurately to get correctly-scaled, meaningful frequency values in the output, rather than the default of 1.0, which treats each sample as one time unit
Tip: The nperseg parameter controls the tradeoff between time resolution and frequency resolution — a shorter segment gives better time resolution, seeing frequency changes more precisely in time, but worse frequency resolution, and vice versa for a longer segment; there's no way to have both simultaneously to unlimited precision.
from scipy import signal
import numpy as np
fs = 1000
t = np.linspace(0, 1, fs, endpoint=False)
x = np.sin(2 * np.pi * 50 * t)
frequencies, times, Sxx = signal.spectrogram(x, fs, nperseg=1000)
print(Sxx.shape)