Matplotlib 如何更改pyplot.specgram x轴和y轴缩放?

Matplotlib 如何更改pyplot.specgram x轴和y轴缩放?,matplotlib,signal-processing,spectrogram,Matplotlib,Signal Processing,Spectrogram,我以前从未使用过音频信号,对信号处理知之甚少。然而,我需要使用matplotlib库中的pyplot.specgram函数来表示音频信号。我是这样做的 import matplotlib.pyplot as plt import scipy.io.wavfile as wavfile rate, frames = wavfile.read("song.wav") plt.specgram(frames) 我得到的结果是下面这张漂亮的光谱图: 当我看x轴和y轴时,我认为它们是频率和时间域,我

我以前从未使用过音频信号,对信号处理知之甚少。然而,我需要使用matplotlib库中的
pyplot.specgram
函数来表示音频信号。我是这样做的

import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile

rate, frames = wavfile.read("song.wav")
plt.specgram(frames)
我得到的结果是下面这张漂亮的光谱图:

当我看x轴和y轴时,我认为它们是频率时间域,我无法理解频率从0缩放到1.0,时间从0缩放到80k。 它背后的直觉是什么?更重要的是,如何以人性化的格式表示它,使频率为0到100k,时间以秒为单位?

  • 首先,频谱图表示信号的频谱内容随时间的变化-这是时域波形的频域表示(例如,正弦波、文件“song.wav”或其他任意波-即振幅随时间的变化)

  • 频率值(y轴,赫兹)完全取决于波形的采样频率(“song.wav”),范围为“0”到“采样频率/2”,上限为“奈奎斯特频率”或“折叠频率”()。如果未另行规定,matplotlib specgram函数将自动确定输入波形的采样频率,该频率定义为1/dt,dt是波形离散采样之间的时间间隔。您可以将选项Fs='sampling rate'传递给specgram函数,以手动定义它是什么。如果您自己弄清楚并将这些变量传递给specgram函数,您将更容易了解正在发生的事情

  • 时间值(x轴,秒)完全取决于“song.wav”的长度。如果使用较大的窗口长度来计算每个光谱切片,您可能会注意到一些空白或填充(想想——垂直排列和水平平铺的各个光谱,以创建光谱图图像)

  • 要使轴在绘图中更直观,请使用x轴和y轴标签,还可以使用类似于的方法缩放轴值(即更改单位)

带回家的消息-尽量对代码更详细一点:我的示例见下文

    import matplotlib.pyplot as plt
    import numpy as np

    # generate a 5Hz sine wave
    fs = 50
    t = np.arange(0, 5, 1.0/fs)
    f0 = 5
    phi = np.pi/2
    A = 1
    x = A * np.sin(2 * np.pi * f0 * t +phi)

    nfft = 25

    # plot x-t, time-domain, i.e. source waveform
    plt.subplot(211)
    plt.plot(t, x)
    plt.xlabel('time')
    plt.ylabel('amplitude')

    # plot power(f)-t, frequency-domain, i.e. spectrogram
    plt.subplot(212)
    # call specgram function, setting Fs (sampling frequency) 
    # and nfft (number of waveform samples, defining a time window, 
    # for which to compute the spectra)
    plt.specgram(x, Fs=fs, NFFT=nfft, noverlap=5, detrend='mean', mode='psd')
    plt.xlabel('time')
    plt.ylabel('frequency')
    plt.show()
5Hz_谱图:


正如其他人所指出的,您需要指定采样率,否则您将获得标准化频率(0到1之间)和采样指数(0到80k)。幸运的是,这很简单:

plt.specgram(frames, Fs=rate)
扩展Nukolas答案并结合我的 和 我们不仅可以得到频率轴上的kHz,还可以得到时间轴上的分和秒

import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile

cmap = plt.get_cmap('viridis') # this may fail on older versions of matplotlib
vmin = -40  # hide anything below -40 dB
cmap.set_under(color='k', alpha=None)

rate, frames = wavfile.read("song.wav")
fig, ax = plt.subplots()
pxx, freq, t, cax = ax.specgram(frames[:, 0], # first channel
                                Fs=rate,      # to get frequency axis in Hz
                                cmap=cmap, vmin=vmin)
cbar = fig.colorbar(cax)
cbar.set_label('Intensity dB')
ax.axis("tight")

# Prettify
import matplotlib
import datetime

ax.set_xlabel('time h:mm:ss')
ax.set_ylabel('frequency kHz')

scale = 1e3                     # KHz
ticks = matplotlib.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))
ax.yaxis.set_major_formatter(ticks)

def timeTicks(x, pos):
    d = datetime.timedelta(seconds=x)
    return str(d)
formatter = matplotlib.ticker.FuncFormatter(timeTicks)
ax.xaxis.set_major_formatter(formatter)
plt.show()
结果:


XY比例因子是未知的,无需指定采样率。