Python 如何以与 pylab 的 specgram() 相同的方式绘制频谱图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15961979/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I plot a spectrogram the same way that pylab's specgram() does?
提问by danmcardle
In Pylab, the specgram()function creates a spectrogram for a given list of amplitudes and automatically creates a window for the spectrogram.
在 Pylab 中,该specgram()函数为给定的振幅列表创建频谱图,并自动为频谱图创建一个窗口。
I would like to generate the spectrogram (instantaneous power is given by Pxx), modify it by running an edge detector on it, and then plot the result.
我想生成频谱图(瞬时功率由 给出Pxx),通过在其上运行边缘检测器来修改它,然后绘制结果。
(Pxx, freqs, bins, im) = pylab.specgram( self.data, Fs=self.rate, ...... )
The problem is that whenever I try to plot the modified Pxxusing imshowor even NonUniformImage, I run into the error message below.
问题是,每当我尝试绘制修改后的Pxxusingimshow或 even 时NonUniformImage,都会遇到下面的错误消息。
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/image.py:336: UserWarning: Images are not supported on non-linear axes. warnings.warn("Images are not supported on non-linear axes.")
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/image.py:336:用户警告:非线性轴不支持图像。warnings.warn("非线性轴不支持图像。")
For example, a part of the code I'm working on right is below.
例如,我正在处理的部分代码如下。
# how many instantaneous spectra did we calculate
(numBins, numSpectra) = Pxx.shape
# how many seconds in entire audio recording
numSeconds = float(self.data.size) / self.rate
ax = fig.add_subplot(212)
im = NonUniformImage(ax, interpolation='bilinear')
x = np.arange(0, numSpectra)
y = np.arange(0, numBins)
z = Pxx
im.set_data(x, y, z)
ax.images.append(im)
ax.set_xlim(0, numSpectra)
ax.set_ylim(0, numBins)
ax.set_yscale('symlog') # see http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_yscale
ax.set_title('Spectrogram 2')
Actual Question
实际问题
How do you plot image-like data with a logarithmic y axis with matplotlib/pylab?
你如何用 matplotlib/pylab 用对数 y 轴绘制类似图像的数据?
采纳答案by Joe Kington
Use pcoloror pcolormesh. pcolormeshis much faster, but is limited to rectilinear grids, where as pcolor can handle arbitrary shaped cells. (It uses specgramuses pcolormesh, if I recall correctly.imshow.)
使用pcolor或pcolormesh。pcolormesh速度更快,但仅限于直线网格,而 pcolor 可以处理任意形状的单元格。 (它使用specgram使用pcolormesh,如果我没记错的话。imshow.)
As a quick example:
举个简单的例子:
import numpy as np
import matplotlib.pyplot as plt
z = np.random.random((11,11))
x, y = np.mgrid[:11, :11]
fig, ax = plt.subplots()
ax.set_yscale('symlog')
ax.pcolormesh(x, y, z)
plt.show()


The differences you're seeing are due to plotting the "raw" values that specgramreturns. What specgramactually plots is a scaled version.
您看到的差异是由于绘制了specgram返回的“原始”值。什么specgram实际上地块是成比例版本。
import matplotlib.pyplot as plt
import numpy as np
x = np.cumsum(np.random.random(1000) - 0.5)
fig, (ax1, ax2) = plt.subplots(nrows=2)
data, freqs, bins, im = ax1.specgram(x)
ax1.axis('tight')
# "specgram" actually plots 10 * log10(data)...
ax2.pcolormesh(bins, freqs, 10 * np.log10(data))
ax2.axis('tight')
plt.show()


Notice that when we plot things using pcolormesh, there's no interpolation. (That's part of the point of pcolormesh--it's just vector rectangles instead of an image.)
请注意,当我们使用 绘制事物时pcolormesh,没有插值。(这是重点的一部分pcolormesh——它只是矢量矩形而不是图像。)
If you want things on a log scale, you can use pcolormeshwith it:
如果你想要对数尺度的东西,你可以使用pcolormesh它:
import matplotlib.pyplot as plt
import numpy as np
x = np.cumsum(np.random.random(1000) - 0.5)
fig, (ax1, ax2) = plt.subplots(nrows=2)
data, freqs, bins, im = ax1.specgram(x)
ax1.axis('tight')
# We need to explictly set the linear threshold in this case...
# Ideally you should calculate this from your bin size...
ax2.set_yscale('symlog', linthreshy=0.01)
ax2.pcolormesh(bins, freqs, 10 * np.log10(data))
ax2.axis('tight')
plt.show()


回答by Miss Palmer
Just to add to Joe's answer...
I was getting small differences between the visual output of specgramcompared to pcolormesh(as noisygecko also was) that were bugging me.
只是为了补充乔的答案......specgram与pcolormesh困扰我的视觉输出相比(因为noisegecko也是),我发现了微小的差异。
Turns out that if you pass frequency and time bins returned from specgramto pcolormesh, it treats these values as values on which to centre the rectangles rather than edges of them.
事实证明,如果您传递从specgramto返回的频率和时间箱pcolormesh,它会将这些值视为使矩形居中而不是矩形边缘的值。
A bit of fiddling gets them to allign better (though still not 100% perfect). The colours are identical now also.
稍微摆弄一下可以让它们更好地对齐(尽管仍然不是 100% 完美)。颜色现在也相同。
x = np.cumsum(np.random.random(1024) - 0.2)
overlap_frac = 0
plt.subplot(3,1,1)
data, freqs, bins, im = pylab.specgram(x, NFFT=128, Fs=44100, noverlap = 128*overlap_frac, cmap='plasma')
plt.title("specgram plot")
plt.subplot(3,1,2)
plt.pcolormesh(bins, freqs, 20 * np.log10(data), cmap='plasma')
plt.title("pcolormesh no adj.")
# bins actually returns middle value of each chunk
# so need to add an extra element at zero, and then add first to all
bins = bins+(bins[0]*(1-overlap_frac))
bins = np.concatenate((np.zeros(1),bins))
max_freq = freqs.max()
diff = (max_freq/freqs.shape[0]) - (max_freq/(freqs.shape[0]-1))
temp_vec = np.arange(freqs.shape[0])
freqs = freqs+(temp_vec*diff)
freqs = np.concatenate((freqs,np.ones(1)*max_freq))
plt.subplot(3,1,3)
plt.pcolormesh(bins, freqs, 20 * np.log10(data), cmap='plasma')
plt.title("pcolormesh post adj.")

