I have some 64 channel EEG data sampled at 256Hz and I'm trying to conduct a time frequency analysis for each channel and plot a spectrogram.

The data is stored in a numpy 3d array, where one of the dimensions has length 256, each element containing a microvolt reading over all sampled time points (total length is 1 second for each channel of data)

To be clear: my 3D array is 64*256*913 (electrode * voltages * trial). Trial is just a single trial of an experiment. So what I want to do is take a single electrode, from a single trial, and the entire 1D voltage vector and creating a time-frequency spectrogram. So I want to create a spectrogram plot from data[0,:,0] for example.

For each electrode, I want a plot where the y axis is frequency, x axis is time, and colour/intensity is power

I have tried using this in python:

from matplotlib.pyplot import specgram
#data = np.random.rand(256)
specgram(data, NFFT=256, Fs=256)

This gives me something that looks like this:

enter image description here

Right off the bat this looks incorrect to me because the axis ranges are incorrect

Furthermore, when I run the same code for all EEG channels, over all of my data, I end up with the exact same plot (even though I have verified that the data is different for each)

I'm pretty new to signal processing, is there somewhere that I went wrong in either how my data is laid out or how I used my function?


The idea of a spectogram is to split your signal into a number of blocks or frames, which are potentially overlapping. After windowing, an FFT is calculated per frame. The output of these FFTs are collected as column vectors in your graph. Thus, the x-axis is related to time and the y-axis is related to frequency. Since the FFT of a real-valued signal is symmetric, only half the frequencies have to be plotted. Therefore, the y-axis goes from 0 to Fs/2=128.

Since you have chosen the FFT length (NFFT=256) equal to the lenght of your signal, you have only one full frame of data.

Potential solutions are to reduce your FFT length NFFT or increase your data length. For now, the easiest way seems to be in reducing NFFT. Note however, that you also need to adjust noverlap in that case. A widely used amount of overlap is NFFT/2.

Example: The following command will give you a spectogram.

specgram(data, NFFT=64, Fs=256, noverlap=32)

Alternative implementation:

specgram(data, NFFT=Nfft, Fs=256, noverlap=Nfft/2)

The FFT length parameter NFFT leads to a tradeoff between time and frequency resolution.

EDIT To answer additional questions in the comments.

From the documentation in [http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.specgram]:

Returns the tuple (spectrum, freqs, t, im):

spectrum: 2-D array

columns are the periodograms of successive segments

freqs: 1-D array

The frequencies corresponding to the rows in spectrum

t: 1-D array

The times corresponding to midpoints of segments (i.e the columns in >spectrum)

im: instance of class AxesImage

The image created by imshow containing the spectrogram

Thus, the first element in the returned tuple contains the power values.

  • $\begingroup$ perfect, thanks! Currently I am plotting the resulting spectrogram, but instead of plotting, is it possible to have specgram() return the power value that its plotting at each point? The function seems to return 3 arrays, none of which seem to be long enough to contain all of the power values $\endgroup$
    – Simon
    Aug 7 '15 at 20:04
  • $\begingroup$ I've editted my answer. $\endgroup$
    – Brian
    Aug 8 '15 at 15:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.