🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEscipy

scipy Documentation

LOADING ENGINE...

signal.spectrogram()

AI & DATA SCIENCE // signal-spectrogram

scipy.signal.spectrogram() computes how a signal's frequency content changes over time, by dividing the signal into overlapping segments and computing the frequency spectrum of each.

Syntax

scipy.signal.spectrogram(x, fs=1.0, nperseg=None)

Deep Dive Course

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.

editor.html
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)
localhost:3000

2Practical Example

Here is a real-world application of signal.spectrogram() showing how it is used in production SciPy code.

editor.html
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]))
localhost:3000

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.

editor.html
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)
localhost:3000

Examples

Example 01Basic Usage
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)
Example 02Advanced Example
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]))

Best Practices

  • Use spectrogram() instead of a single full-signal FFT whenever the frequency content is expected to change meaningfully over the duration of the signal
  • 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
  • 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

Interview Question

Why can't a spectrogram achieve perfect resolution in both time and frequency simultaneously?

Hint: Think about the fundamental relationship between a signal's duration and how precisely its frequency content can be determined.

This is a manifestation of a fundamental time-frequency tradeoff, closely related to the uncertainty principle in signal processing: determining a frequency precisely requires observing a signal over a longer duration, since frequency is inherently a property that emerges from a value oscillating over time, but using a longer time segment means you lose precision about exactly when within that window any particular frequency occurred. A spectrogram's nperseg parameter directly controls this tradeoff — a shorter segment localizes events more precisely in time but blurs frequency detail, while a longer segment sharpens frequency detail at the cost of blurring exactly when it occurred, and no choice of segment length escapes this fundamental tradeoff entirely.

Exercises

MediumPractice using signal.spectrogram() in a real scenario.
View Solution
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)

Frequently Asked Questions

Why can't a spectrogram achieve perfect resolution in both time and frequency simultaneously?

This is a manifestation of a fundamental time-frequency tradeoff, closely related to the uncertainty principle in signal processing: determining a frequency precisely requires observing a signal over a longer duration, since frequency is inherently a property that emerges from a value oscillating over time, but using a longer time segment means you lose precision about exactly when within that window any particular frequency occurred. A spectrogram's nperseg parameter directly controls this tradeoff — a shorter segment localizes events more precisely in time but blurs frequency detail, while a longer segment sharpens frequency detail at the cost of blurring exactly when it occurred, and no choice of segment length escapes this fundamental tradeoff entirely.

Related Functions

signal-convolvesignal-lfilternp-sin