plotting single-channel epochs overlaid


I needed to plot overlaid single-channel epochs for someone today (see plot below). I couldn’t figure out a clean way to do it, and ended up slicing epochs._data.

I was wondering if there is an actual API for such a plot?


I don’t think there’s an API for this… it might have been possible to do epochs.iter_evoked() and then pass the result to plot_compare_evokeds perhaps?

Do you think it’s a common enough need that it’s worth implementing one, or is this likely a one-off workflow for you? Something like plot_epochs_overlay() with options show_epochs=bool, show_mean=bool, ci=float|bool|callable|None (where ci mirrors the ci argument in plot_compare_evokeds) seems reasonable

Thanks @drammock!

I don’t know how common this kind of plot is. Our lab has two papers in the pipeline that happen to need it. I was a bit surprised that it’s not implemented.

Of course it’s also clear that mne cannot support every possible plot out of the box. Working with _data is fine, except for the fact that you’re working with undocumented internals and therefore the semantics are a bit unclear (e.g. does baselining directly affect _data or not)?

To work with a documented function instead, you could use inst.get_data() methods which has a couple of handy arguments to return only relevant parts of the underlying ._data array.

I think baseline correction directly affects ._data at initialization of an Epoch instance, except if baseline=None was passed.

@mscheltienne good point about get_data(), somehow missed it.

For the record, plot_compare_evokeds won’t get you all the way there. Here’s how far it gets you:

import os
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
raw =, verbose=False, preload=False)
events = mne.find_events(raw, stim_channel='STI 014')
event_dict = {'auditory/left': 1}
epochs = mne.Epochs(raw, events, event_id=event_dict)

evk_list = {'aud/left': list(epochs.iter_evoked())}

mne.viz.plot_compare_evokeds(evk_list, picks=7)

(no individual traces, just the mean and CI). However, it’s not too bad using matplotlib / Seaborn:

import matplotlib.pyplot as plt
import seaborn as sns

# note: this scales grads to fT/cm by default! see `scalings` param
df = epochs.to_data_frame(picks='MEG 0133', time_format=None)

fig, ax = plt.subplots()
kwargs = dict(x='time', y='MEG 0133', data=df, ax=ax)
sns.lineplot(**kwargs, units='epoch', estimator=None, linewidth=0.5, alpha=0.2)
sns.lineplot(**kwargs, ci='sd', linewidth=2, color='r')


It needs a little tweaking to get the CI to be clearly visible against all the individual epoch traces, but even so I’m not super motivated to implement this within MNE-Python, knowing that it’s not too bad to do it manually. But I probably wouldn’t say “no” if a motivated user wanted to do the work of implementing it.

1 Like