mne-python: Indexing error in Evoked.plot_topomap() when trying to mask channels

So either this is a documentation problem or a code bug: I supply times and mask with a shape of (n_times, n_chans) to Evoked.plot_topomap(), and it crashes with an IndexError.

# %%
from pathlib import Path
import numpy as np

import mne

sample_data_dir = Path(mne.datasets.sample.data_path())
fname = sample_data_dir / 'MEG' / 'sample' / 'sample_audvis-ave.fif'

condition = 'Left Auditory'
evoked = mne.read_evokeds(fname=fname, condition=condition)
evoked.pick('mag')
evoked.apply_baseline((None, 0))

times = (0.090, 0.110)
mask = np.empty(
    [len(evoked.ch_names), len(times)]
)
mask.fill(False)
mask[20, :] = True

fig = evoked.plot_topomap(times=times, time_unit='s', mask=mask)
Traceback (most recent call last):
  File "/private/tmp/mwe_topo.py", line 22, in <module>
    fig = evoked.plot_topomap(times=times, time_unit='s', mask=mask)
  File "/Users/hoechenberger/Development/mne-python/mne/evoked.py", line 478, in plot_topomap
    return plot_evoked_topomap(
  File "/Users/hoechenberger/Development/mne-python/mne/viz/topomap.py", line 1697, in plot_evoked_topomap
    mask_ = mask[np.ix_(picks, time_idx)]
IndexError: index 174 is out of bounds for axis 1 with size 2

I expected a topoplot at the two time points, showing only the first 20 channels.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 25 (25 by maintainers)

Most upvoted comments

@drammock Thanks, I completely missed point 1 in this discussion. Yes, to plot topomap based on only some of the sensors you need to drop the remaining ones and use extrapolate='local'. @mscheltienne .astype() would need copy=False, so that the mask is not copied if it has the correct dtype.

The code snippet posted by @drammock could be a part of a short example on masking channels with evoked.plot() and plot_topomap(). It could also be added to this example/tutorial on topomap plotting: https://mne.tools/stable/auto_examples/visualization/evoked_topomap.html#sphx-glr-auto-examples-visualization-evoked-topomap-py.

sorry to be super-late chiming in on this issue. Here is my impression:

  1. the intent of mask is only to affect the drawing of sensor markers on the plot. It is not intended to affect the field interpolation. (I think if you want to see field interpolation based on a subset of sensors it should work to drop all other sensors first before plotting?)
  2. the docstring of evoked.plot_topomap() should be updated to emphasize that mask must match the shape of evoked.data
  3. if you want to specify mask as (n_channels, n_displayed_times) then I agree with @mmagnuski that we’d want to hear an example use case where this would make sense. Note that you can still make it work:
mask = np.zeros(evoked.data.shape, dtype='bool')
my_times = (0.09, 0.11)
my_signif_channels = [('MEG 2541', 'MEG 2331'), ('MEG 0111', 'MEG 0121', 'MEG 0131')]
which_times = np.searchsorted(evoked.times, my_times) - 1
which_channels = [np.in1d(evoked.ch_names, my_ch) for my_ch in my_signif_channels]
for _chs, _time in zip(which_channels, which_times):
    mask[_chs, _time] = True
fig = evoked.plot_topomap(times=my_times, time_unit='s', mask=mask, mask_params=dict(markersize=10, markerfacecolor='y'))

fig1

@hoechenberger That depends on what kind of mask you have - if you got it from cluster-based test on the whole time range (all time samples) then it makes sense to me.