Skip to content

Default scaling of x-axis in specgram() is incorrect (i.e. the default value for the xextent parameter) #7666

Closed
@DietBru

Description

@DietBru

When drawing a spectrogram with the matplotlib.pyplot.specgram(), there's a systematic error when using the default value xextent=None, .i.e., xextent=(0, max(bins). Since the x-position is always the center an not the left of the FFT-window, the correct value is xextent=(min(bins), max(bins)).
The consequences can be demonstrated, when plotting the spectrogram of a linear chirp signal. This is the default behavior:
spec_default

This is is the desired behavior:
spec_correct

Those two images were generated by the following code::

import numpy as np
import sympy as sy
import matplotlib.pyplot as plt


t_sy = sy.Symbol("t", real=True)
fi_sy = 2 + 2*t_sy  # instantanenous frequency
phi_sy = fi_sy.integrate(t_sy) # instantaneous

fi_np = sy.lambdify(t_sy, fi_sy)  # numpy functions
phi_np = sy.lambdify(t_sy, phi_sy)
lb_fi = "$f_i(t)=%s$" % sy.latex(fi_sy)  # plot label

n_x = 2**10
T_x = 10/n_x
t_x = np.arange(n_x)*T_x

x = np.sin(2*np.pi*phi_np(t_x))  # generate signals
f_Xos, Xos = np.fft.rfftfreq(n_x*2, T_x), np.fft.rfft(x, n=2*n_x)

# Parameter for specgram:
nfft = 256 # Parameter NFFT
sargs = dict(x=2/nfft * x, NFFT=nfft, Fs=1/T_x, noverlap=nfft-1,
                   mode="magnitude", scale="linear", cmap=plt.cm.viridis)
fg1, ax1 = plt.subplots(1, 1)
fg2, ax2 = plt.subplots(1, 1)
ax1.set_title(r"256 Point Spectrogram with default 'xextent' parameter")
_, _, t_s1, im1 = ax1.specgram(**sargs)
ax2.set_title(r"256 Point Spectrogram with correct 'xextent' parameter")
_, _, t_s2, im2 = ax2.specgram(**sargs, xextent=(t_s1[0], t_s1[-1]))

for fg, ax, im in [(fg1, ax1, im1), (fg2, ax2, im2)]:
    ax.plot(t_x, fi_np(t_x), "r--", label=lb_fi, linewidth=.5)
    cb = fg.colorbar(im)
    cb.set_label("Magnitude $|X(t,f)|$")
    ax.legend()
    ax.set_xlim(t_x[0], t_x[-1])
    ax.set_xlabel(r"Time $t$ in Seconds ($\Delta t = %.3f\,$s)" % t_x[1])
    ax.set_ylabel(r"Frequency $f$ in Hertz ($\Delta f = %.3f\,$Hz)" % f_Xos[1])

plt.show()

Version Information:
Matplotlib 2.0.0rc2
Python 3.5.2 | packaged by conda-forge | (default, Sep 8 2016, 14:23:11)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)] on linux

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions