Search code examples
pythonmatplotlibfft

Matplotlib - Wrong number of Frequency bins with Specgram


From what I unserstand, in a FFT the number of frequency bins is exactly the number of samples / 2.

However, matplotlib.specgram gives me one bin too much.

fig, [ax1, ax2, ax3] = plt.subplots(nrows=3, ncols=1)    
Pxx, freqs, times, im = ax3.specgram(raw_normalized, NFFT=1024, Fs=sampleRate, noverlap=overlap, window=matplotlib.mlab.window_none, scale='linear')

Pxx contains the array of the spectogram, which should have 512 bins (due to the number of samples set to 1024), however it has 513. Is there something off with my unserstanding off FFTs, or is there something wrong/quirky in the matplotlib library?


Solution

  • Just a wild guess, but if you set NFFT to 1024, it should have 1024 bins. However, if your input is real-valued, the values should be symmetrical. The 0th value should be the DC value, and the 512th value should be the value with the highest frequency. The 511th and 513th should be identical, so the spectrogram might filter out the symmetric values, as it knows the input is real-valued. So you get 513 values. (because the 513th to 1023th values are hidden; starting count at #0, of course)

    The reasoning behind that is that the FFT folds a 'rotating' value on top of your data. It starts rotation slowly, #0 is the dc value, followed by #1, which is one rotation on the entire data. #2 is two rotations, and so on.

    #512 is 512 rotations on your data of 1024 points, meaning you get one full rotation every 2 samples. This is the nyquist frequency for your data, everything above that will be subject to aliasing. Therefore the #513 looks identically to #511, just rotating in reverse. #1023 is identical to #1, just a single rotation, but in the opposite direction.

    For complex-valued data, folding with a clockwise rotation and a counter clockwise rotation makes a difference, but for real-valued data it is the same. Therefore values #513 to #1023 can be discarded, leaving you with 513 meaningful buckets.

    Another detail: Technically, the output values of the FFT are always complex, even with real-valued inputs, and contain both a magnitude and a phase information, but your library probably filters out the phase information and just gives the magnitude, to convert it back to real-valued output values.