【问题标题】:Shifting spectrogram on x-axis在 x 轴上移动频谱图
【发布时间】:2021-11-28 06:39:29
【问题描述】:

我的信号从 -1 秒开始。

但是,在绘制频谱图时,第一个 bin 边缘从 0 开始(中点在 0.25)如何更改它以便在 x 轴上绘制时准确地表示我的数据?

使用xextent=(time[0] + 0.125, time[-1]) 似乎可以解决这个问题。但是,我不确定是什么变量决定了 bin 宽度,因此担心这可能会随着不同采样率、点数等的其他数据集而改变。

示例代码:

import numpy as np
import matplotlib.pyplot as plt
import signal

time = np.linspace(-1, 16, 65536)
signal = np.sin(2 * np.pi * time)

fig = plt.figure()

ax_top = fig.add_subplot(211)
ax_spec = fig.add_subplot(212)

ax_top.plot(time, signal)
ax_top.set_xlim(-1, 10)
ax_spec.set_xlim(-1, 10)

Pxx, freqs, bins, cax = ax_spec.specgram(signal, NFFT=2048, Fs=4096, noverlap=2048 / 2, mode='magnitude', pad_to=2048 * 16)

ax_spec.set_xticks(np.arange(time[0], time[-1], 1))
ax_top.set_xticks(np.arange(time[0], time[-1], 1))

plt.show()

【问题讨论】:

  • 显示绘图代码。你可以随心所欲地制作你的 x 数据
  • 这似乎是一个愚蠢的问题,但 Bins 是频谱图函数的输出,然后将其绘制在 x 轴上。但是如何在绘制 bins 数组之前对其进行修改?理想情况下,我会在绘制之前从 bins 数组中减去 1。
  • 你可以绘制bins + time[0]
  • 你能发一个minimal reproducible example吗?所以随机生成的数据集,用于 x 数据的 linspace 左右,以及足够的代码来制作上面的图,没有任何额外的东西?在发布答案之前,我想了解情节中的不同元素。
  • 我认为这不是正确的方法。 xextent 可能是要走的路,因为它会通知imshowextent,但我想在回答之前先玩一下。您能否发布上面的 MCVE,而不是您的项目特定示例?

标签: python matplotlib signal-processing spectrogram


【解决方案1】:

我将稍微简化您的示例,减少手动设置,以确保轴正确地开箱即用:

import numpy as np
import matplotlib.pyplot as plt

time = np.linspace(-1, 16, 65536)
signal = np.sin(2 * np.pi * time)

fig, ax = plt.subplots(constrained_layout=True)
# In your case, use this instead:
#fig, (ax_top, ax_spec) = plt.subplots(2, 1, constrained_layout=True)

ax.plot(time, 1024 * signal + 1024, c='r')
Pxx, freqs, bins, cax = ax.specgram(signal, NFFT=2048, Fs=4096, noverlap=2048/2, mode='magnitude', pad_to=2048*16)
plt.show()

目标是使信号和图像的时间轴对齐。

频谱图代码的重要部分在matplotlib.mlab._spectral_helper中实现。频谱图中的每一列都来自matplotlib.mlab._stride_windows 创建的时间窗口。时间窗口为NFFTsamples 宽,因此nth 时间箱的中心位于0.5 * (time[0] + time[NFFT - 1]) + n * (NFFT - noverlap) * dt。你可以在mlab._spectral_helper 中看到这个计算。唯一的区别是dt被假定为1 / Fs,并且起点被假定为零。

回到specgram 的代码,您可以看到xextent 仅在您不手动设置时才使用必要的半像素填充。总而言之,这意味着您的图像只是移动了-time[0]。您可以使用xextent 模拟相同的班次。

在我展示如何做到这一点之前,请记住您的 Fs 值不正确:

>>> 1 / np.diff(time).mean()
3855.0

发生这种情况是因为Fs 应该是(len(time) - 1) / (time[-1] - time[0]),不管其他什么。您可以从两个样本案例中轻松直观地理解这一点。当您手动将半像素填充应用于范围的边缘时,这将很有用。下面的填充代码直接取自xextent = None:

time = np.linspace(-1, 16, 65536)
signal = np.sin(2 * np.pi * time)
fs = (len(time) - 1) / (time[-1] - time[0])
half_pixel = 512 / fs   # (NFFT-noverlap) / Fs / 2
half_bin = 1024 / fs  # NFFT / Fs / 2
xextent = (time[0] - half_pixel + half_bin, time[-1] + half_pixel - half_bin)

fig, ax = plt.subplots(constrained_layout=True)

ax.plot(time, (fs / 4) * (signal + 1), c='r')
Pxx, freqs, bins, cax = ax.specgram(signal, NFFT=2048, Fs=fs, noverlap=2048 / 2, mode='magnitude', pad_to=2048 * 16, xextent=xextent)
plt.show()

果然,图像在信号图上完美居中,信号的末端正好伸出图像边缘的半个时间窗口:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    • 1970-01-01
    • 2023-02-14
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    相关资源
    最近更新 更多