Spectrograms with python matplotlib


I wanted to share some code to generate spectrograms of muse EEG data, and also ask for suggestions on best practices. Here’s a simple script that generates spectrograms of the 4 EEG channels from an ASCII OSC output file (generated by muse-player -O):


Here are some spectrograms that it generates:

(zoom/maximize the image for effective viewing)

This was a session where I had my eyes open for 10 seconds, closed for the next 30 seconds, open for 10 seconds, closed again for about 25 seconds, and open for the rest of the session. I can clearly see increased alpha at TP9 and TP10 when my eyes are closed. The spectrograms use 256-length segments for the FFT. Does anyone have any recommendations on tuning this or other specgram() parameters to improve the visual appearance of the spectrograms?


Hey Kartik,

Cool! Glad to see you’re making progress and experimenting. Like you said, you can very clearly see increased Alpha power when you close your eyes, which is expected for most people. Also, when your eyes are open, you can observe the appearance of wider band signals likely due to ocular and muscular artifacts. Thanks for sharing your code.

Have you tried subtracting the mean from the signal before computing spectrogram? The EEG data from Muse will always have some fairly large DC offset, and this will show up as a huge spike at and close to 0Hz in your spectrum. If you subtract the mean from the EEG data first, you might be able to observe the other frequencies more clearly, as they won’t be so small relative to DC.

Also, just a slightly pedantic note: the units of frequency you have there are labelled as kHz, which is not really possible with a sampling rate of 220Hz. It’s probably just a typo and you may already know this, but if not, take a look at the concept of aliasing and the Nyquist Theorem: http://en.wikipedia.org/wiki/Nyquist–Shannon_sampling_theorem


The KHz was absent-minded typing, good catch :slight_smile: Thanks very much for the suggestion to subtract the mean, this definitely helps! I took a DSP class in college many years ago, but have long since forgotten most of it :slight_smile: Maybe some of it will come back to me now. I have uploaded updated versions of the code and spectrograms at the same links as above.

On a related note – I’m working my way towards seeing if I can run event-related potential experiments with the Muse EEG. I notice that with most ERP experiments, the signals vary from about -5 to 5 microvolts, where there are clearly identifiable positive and negative deflections. Is there a “proper” way to normalize the EEG signal data to be in this range, apart from subtracting the mean afterwards? Is that what the drlref data is supposed to be used for? (If not, what is the drlref data used for exactly?) I realize that one of the challenges with ERP experiments is choosing the reference signal, but presumably this data has already been referenced (with respect to FPZ?) and I want to first make use of this data in its default form. Sorry if this question is a little incoherent, I’m trying to make progress on a number of neuroscience learning curves at once :slight_smile:


Looks Great [B]kartik_subbarao[/B]


Tom, do you have any thoughts about my EEG normalizing/referencing questions? Let me know if you’d like me to rephrase/clarify.


Hi Kartik,

Yes, the reference is located at Fpz. The DRL/REF data is provided by MuseIO to be used as an indication of whether or not the three centre electrodes are making contact with the forehead. If they’re making good contact, the two values should be very similar. If they’re different, then the headband is almost certainly not contacting the forehead properly, and the /muse/eeg data is just noise.

The /muse/eeg data from MuseIO is all already in uV, so in that sense it’s in the same form as the data typically used in the EEG literature. While ERPs may tend to have a fairly consistent amplitude (as well as deflection and latency), there will be other signals in the Muse data that are larger, such as ocular and muscular artifacts, environmental noise, etc. This is always the case with EEG data.

ERP experiments usually rely on averaging repeated measurements to make ERPs visible. For real-time detection of ERPs in a BCI (i.e. single trial ERPs), it’s typically necessary to train some kind of classifier. So “single trial ERP” is a useful search term, and you might specifically try reading about P300 spellers and the different approaches to them, because there is a fair amount of activity in that research area.

Google Scholar is a really useful resource for articles on ERPs, both for general information and for really specific applications/experiments. There are also several books on the subject that you can get online. I’ve got a copy of [I]The Oxford Handbook of Event-Related Potential Components[/I], and it’s a pretty expansive, detailed overview of the subject.


Thanks for the response. I’m still not following something though – why are the voltages consistently in the 800 uV range? TP9,FP1,FP2,TP10 aren’t consistently higher than FPz by 800-some microvolts, right? Clearly I’m missing something – sorry if it’s something obvious and I’m not catching on.

Also, is it possible to re-reference the data to, say, TP9, and get access to the FPz data?


There will always be a DC offset in the EEG data. The offset is in REF as well as the data, because due to the amplifier configuration in Muse the DC difference is not amplified, just the AC difference is. The offset appears un-amplified at the input to our ADC, and is approximately at the ADC’s midpoint. If you use the conversion factor that we mention in our documentation to convert amplified ADC values to uV, this comes out to around 800uV. However, the conversion factor is calculated to account for AC signals that have been amplified, and remember, the DC offset does not get amplified.

So the 800uV mean is not [I]really[/I] 800uV. But it’s not [I]really[/I] the mean that you’re interested in. The long-term mean of the signal is probably useful for only a few things like the skin-electrode half cell potential. Really what you’re interested in is the peak-to-peak amplitude of signals, whether they be slow or fast-changing. Even if you want to investigate the behaviour of a signal which changes very slowly, the useful information is contained in how it changes, not in what mean it is centred around.

Yes, you can re-reference the data to whatever electrode you like. Just be aware that artifacts in your reference electrode will affect all of your electrodes. Muse was designed specifically to maintain a mechanically stable reference on the forehead. The ear electrodes are more prone to movement.


Thanks for the in-depth explanation Tom.


Hi kartik_subbarao,

I am running your python script on my own file but when the plots are shown it only plots 6 seconds instead of 120 seconds. Would you know any reason why this might happen?



Not sure – I don’t see anything in the code that limits the time, it should be plotting all of the data that you have collected successfully in the file.