In Pylab, the `specgram()`

function creates a spectrogram for a given list of amplitudes and automatically creates a window for the spectrogram.

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, freqs, bins, im) = pylab.specgram( self.data, Fs=self.rate, ...... )
```

The problem is that whenever I try to plot the modified `Pxx`

using `imshow`

or even `NonUniformImage`

, I run into the error message below.

/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.")

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')
```

How do you plot image-like data with a logarithmic y axis with matplotlib/pylab?

Use `pcolor`

or `pcolormesh`

. `pcolormesh`

is much faster, but is limited to rectilinear grids, where as pcolor can handle arbitrary shaped cells. (It uses `specgram`

uses `pcolormesh`

, if I recall correctly.`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 `specgram`

returns. What `specgram`

actually plots is a scaled version.

```
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.)

If you want things on a log scale, you can use `pcolormesh`

with it:

```
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()
```

Just to add to Joe's answer...
I was getting small differences between the visual output of `specgram`

compared to `pcolormesh`

(as noisygecko also was) that were bugging me.

Turns out that if you pass frequency and time bins returned from `specgram`

to `pcolormesh`

, it treats these values as values on which to centre the rectangles rather than edges of them.

A bit of fiddling gets them to allign better (though still not 100% perfect). The colours are identical now also.

```
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.")
```

Licensed under: CC-BY-SA with attribution

Not affiliated with: Stack Overflow