def test_plot_topomap_basic(monkeypatch): """Test basics of topomap plotting.""" evoked = read_evokeds(evoked_fname, 'Left Auditory', baseline=(None, 0)) res = 8 fast_test = dict(res=res, contours=0, sensors=False, time_unit='s') fast_test_noscale = dict(res=res, contours=0, sensors=False) ev_bad = evoked.copy().pick_types(meg=False, eeg=True) ev_bad.pick_channels(ev_bad.ch_names[:2]) plt_topomap = partial(ev_bad.plot_topomap, **fast_test) plt_topomap(times=ev_bad.times[:2] - 1e-6) # auto, plots EEG pytest.raises(ValueError, plt_topomap, ch_type='mag') pytest.raises(ValueError, plt_topomap, times=[-100]) # bad time pytest.raises(ValueError, plt_topomap, times=[[0]]) # bad time evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms') # extrapolation to the edges of the convex hull or the head circle evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='local') evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='head') evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='head', outlines='skirt') # extrapolation options when < 4 channels: temp_data = np.random.random(3) picks = channel_indices_by_type(evoked.info)['mag'][:3] info_sel = pick_info(evoked.info, picks) plot_topomap(temp_data, info_sel, extrapolate='local', res=res) plot_topomap(temp_data, info_sel, extrapolate='head', res=res) # make sure extrapolation works for 3 channels with border='mean' # (if extra points are placed incorrectly some of them have only # other extra points as neighbours and border='mean' fails) plot_topomap(temp_data, info_sel, extrapolate='local', border='mean', res=res) # border=0 and border='mean': # --------------------------- ch_pos = np.array( sum(([[0, 0, r], [r, 0, 0], [-r, 0, 0], [0, -r, 0], [0, r, 0]] for r in np.linspace(0.2, 1.0, 5)), [])) rng = np.random.RandomState(23) data = np.full(len(ch_pos), 5) + rng.randn(len(ch_pos)) info = create_info(len(ch_pos), 250, 'eeg') ch_pos_dict = {name: pos for name, pos in zip(info['ch_names'], ch_pos)} dig = make_dig_montage(ch_pos_dict, coord_frame='head') info.set_montage(dig) # border=0 ax, _ = plot_topomap(data, info, extrapolate='head', border=0, sphere=1) img_data = ax.get_array().data assert np.abs(img_data[31, 31] - data[0]) < 0.12 assert np.abs(img_data[0, 0]) < 1.5 # border='mean' ax, _ = plot_topomap(data, info, extrapolate='head', border='mean', sphere=1) img_data = ax.get_array().data assert np.abs(img_data[31, 31] - data[0]) < 0.12 assert img_data[0, 0] > 5 # error when not numeric or str: error_msg = 'border must be an instance of numeric or str' with pytest.raises(TypeError, match=error_msg): plot_topomap(data, info, extrapolate='head', border=[1, 2, 3]) # error when str is not 'mean': error_msg = "The only allowed value is 'mean', but got 'fancy' instead." with pytest.raises(ValueError, match=error_msg): plot_topomap(data, info, extrapolate='head', border='fancy') # test channel placement when only 'grad' are picked: # --------------------------------------------------- info_grad = evoked.copy().pick('grad').info n_grads = len(info_grad['ch_names']) data = np.random.randn(n_grads) img, _ = plot_topomap(data, info_grad) # check that channels are scattered around x == 0 pos = img.axes.collections[-1].get_offsets() prop_channels_on_the_right = (pos[:, 0] > 0).mean() assert prop_channels_on_the_right < 0.6 # other: # ------ plt_topomap = partial(evoked.plot_topomap, **fast_test) plt.close('all') axes = [plt.subplot(221), plt.subplot(222)] plt_topomap(axes=axes, colorbar=False) plt.close('all') plt_topomap(times=[-0.1, 0.2]) plt.close('all') evoked_grad = evoked.copy().crop(0, 0).pick_types(meg='grad') mask = np.zeros((204, 1), bool) mask[[0, 3, 5, 6]] = True names = [] def proc_names(x): names.append(x) return x[4:] evoked_grad.plot_topomap(ch_type='grad', times=[0], mask=mask, show_names=proc_names, **fast_test) assert_equal(sorted(names), ['MEG 011x', 'MEG 012x', 'MEG 013x', 'MEG 014x']) mask = np.zeros_like(evoked.data, dtype=bool) mask[[1, 5], :] = True plt_topomap(ch_type='mag', outlines=None) times = [0.1] plt_topomap(times, ch_type='grad', mask=mask) plt_topomap(times, ch_type='planar1') plt_topomap(times, ch_type='planar2') plt_topomap(times, ch_type='grad', mask=mask, show_names=True, mask_params={'marker': 'x'}) plt.close('all') with pytest.raises(ValueError, match='number of seconds; got -'): plt_topomap(times, ch_type='eeg', average=-1e3) with pytest.raises(TypeError, match='number of seconds; got type'): plt_topomap(times, ch_type='eeg', average='x') p = plt_topomap(times, ch_type='grad', image_interp='bilinear', show_names=lambda x: x.replace('MEG', '')) subplot = [x for x in p.get_children() if 'Subplot' in str(type(x))] assert len(subplot) >= 1, [type(x) for x in p.get_children()] subplot = subplot[0] have_all = all('MEG' not in x.get_text() for x in subplot.get_children() if isinstance(x, matplotlib.text.Text)) assert have_all # Plot array for ch_type in ('mag', 'grad'): evoked_ = evoked.copy().pick_types(eeg=False, meg=ch_type) plot_topomap(evoked_.data[:, 0], evoked_.info, **fast_test_noscale) # fail with multiple channel types pytest.raises(ValueError, plot_topomap, evoked.data[0, :], evoked.info) # Test title def get_texts(p): return [ x.get_text() for x in p.get_children() if isinstance(x, matplotlib.text.Text) ] p = plt_topomap(times, ch_type='eeg', average=0.01) assert_equal(len(get_texts(p)), 0) p = plt_topomap(times, ch_type='eeg', title='Custom') texts = get_texts(p) assert_equal(len(texts), 1) assert_equal(texts[0], 'Custom') plt.close('all') # delaunay triangulation warning plt_topomap(times, ch_type='mag') # projs have already been applied pytest.raises(RuntimeError, plot_evoked_topomap, evoked, 0.1, 'mag', proj='interactive', time_unit='s') # change to no-proj mode evoked = read_evokeds(evoked_fname, 'Left Auditory', baseline=(None, 0), proj=False) fig1 = evoked.plot_topomap('interactive', 'mag', proj='interactive', **fast_test) _fake_click(fig1, fig1.axes[1], (0.5, 0.5)) # click slider data_max = np.max(fig1.axes[0].images[0]._A) fig2 = plt.gcf() _fake_click(fig2, fig2.axes[0], (0.075, 0.775)) # toggle projector # make sure projector gets toggled assert (np.max(fig1.axes[0].images[0]._A) != data_max) with monkeypatch.context() as m: # speed it up by not actually plotting m.setattr(topomap, '_plot_topomap', lambda *args, **kwargs: (None, None, None)) with pytest.warns(RuntimeWarning, match='More than 25 topomaps plots'): plot_evoked_topomap(evoked, [0.1] * 26, colorbar=False) pytest.raises(ValueError, plot_evoked_topomap, evoked, [-3e12, 15e6], time_unit='s') for ch in evoked.info['chs']: if ch['coil_type'] == FIFF.FIFFV_COIL_EEG: ch['loc'].fill(0) # Remove extra digitization point, so EEG digitization points # correspond with the EEG electrodes del evoked.info['dig'][85] # Plot skirt evoked.plot_topomap(times, ch_type='eeg', outlines='skirt', **fast_test) # Pass custom outlines without patch eeg_picks = pick_types(evoked.info, meg=False, eeg=True) pos, outlines = _get_pos_outlines(evoked.info, eeg_picks, 0.1) evoked.plot_topomap(times, ch_type='eeg', outlines=outlines, **fast_test) plt.close('all') # Test interactive cmap fig = plot_evoked_topomap(evoked, times=[0., 0.1], ch_type='eeg', cmap=('Reds', True), title='title', **fast_test) fig.canvas.key_press_event('up') fig.canvas.key_press_event(' ') fig.canvas.key_press_event('down') cbar = fig.get_axes()[0].CB # Fake dragging with mouse. ax = cbar.cbar.ax _fake_click(fig, ax, (0.1, 0.1)) _fake_click(fig, ax, (0.1, 0.2), kind='motion') _fake_click(fig, ax, (0.1, 0.3), kind='release') _fake_click(fig, ax, (0.1, 0.1), button=3) _fake_click(fig, ax, (0.1, 0.2), button=3, kind='motion') _fake_click(fig, ax, (0.1, 0.3), kind='release') fig.canvas.scroll_event(0.5, 0.5, -0.5) # scroll down fig.canvas.scroll_event(0.5, 0.5, 0.5) # scroll up plt.close('all') # Pass custom outlines with patch callable def patch(): return Circle((0.5, 0.4687), radius=.46, clip_on=True, transform=plt.gca().transAxes) outlines['patch'] = patch plot_evoked_topomap(evoked, times, ch_type='eeg', outlines=outlines, **fast_test) # Remove digitization points. Now topomap should fail evoked.info['dig'] = None pytest.raises(RuntimeError, plot_evoked_topomap, evoked, times, ch_type='eeg', time_unit='s') plt.close('all') # Error for missing names n_channels = len(pos) data = np.ones(n_channels) pytest.raises(ValueError, plot_topomap, data, pos, show_names=True) # Test error messages for invalid pos parameter pos_1d = np.zeros(n_channels) pos_3d = np.zeros((n_channels, 2, 2)) pytest.raises(ValueError, plot_topomap, data, pos_1d) pytest.raises(ValueError, plot_topomap, data, pos_3d) pytest.raises(ValueError, plot_topomap, data, pos[:3, :]) pos_x = pos[:, :1] pos_xyz = np.c_[pos, np.zeros(n_channels)[:, np.newaxis]] pytest.raises(ValueError, plot_topomap, data, pos_x) pytest.raises(ValueError, plot_topomap, data, pos_xyz) # An #channels x 4 matrix should work though. In this case (x, y, width, # height) is assumed. pos_xywh = np.c_[pos, np.zeros((n_channels, 2))] plot_topomap(data, pos_xywh) plt.close('all') # Test peak finder axes = [plt.subplot(131), plt.subplot(132)] evoked.plot_topomap(times='peaks', axes=axes, **fast_test) plt.close('all') evoked.data = np.zeros(evoked.data.shape) evoked.data[50][1] = 1 assert_array_equal(_find_peaks(evoked, 10), evoked.times[1]) evoked.data[80][100] = 1 assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 100]]) evoked.data[2][95] = 2 assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 95]]) assert_array_equal(_find_peaks(evoked, 1), evoked.times[95]) # Test excluding bads channels evoked_grad.info['bads'] += [evoked_grad.info['ch_names'][0]] orig_bads = evoked_grad.info['bads'] evoked_grad.plot_topomap(ch_type='grad', times=[0], time_unit='ms') assert_array_equal(evoked_grad.info['bads'], orig_bads) plt.close('all')
def test_plot_topomap_basic(): """Test basics of topomap plotting.""" evoked = read_evokeds(evoked_fname, 'Left Auditory', baseline=(None, 0)) res = 8 fast_test = dict(res=res, contours=0, sensors=False, time_unit='s') fast_test_noscale = dict(res=res, contours=0, sensors=False) ev_bad = evoked.copy().pick_types(meg=False, eeg=True) ev_bad.pick_channels(ev_bad.ch_names[:2]) plt_topomap = partial(ev_bad.plot_topomap, **fast_test) plt_topomap(times=ev_bad.times[:2] - 1e-6) # auto, plots EEG pytest.raises(ValueError, plt_topomap, ch_type='mag') with pytest.deprecated_call(match='head_pos'): plt_topomap(head_pos='foo') pytest.raises(ValueError, plt_topomap, times=[-100]) # bad time pytest.raises(ValueError, plt_topomap, times=[[0]]) # bad time evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms') # extrapolation to the edges of the convex hull or the head circle evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='local') evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='head') evoked.plot_topomap([0.1], ch_type='eeg', scalings=1, res=res, contours=[-100, 0, 100], time_unit='ms', extrapolate='head', outlines='skirt') # extrapolation options when < 4 channels: temp_data = np.random.random(3) picks = channel_indices_by_type(evoked.info)['mag'][:3] info_sel = pick_info(evoked.info, picks) plot_topomap(temp_data, info_sel, extrapolate='local', res=res) plot_topomap(temp_data, info_sel, extrapolate='head', res=res) plt_topomap = partial(evoked.plot_topomap, **fast_test) with pytest.deprecated_call(match='layout'): plt_topomap(0.1, layout=layout, scalings=dict(mag=0.1)) plt.close('all') axes = [plt.subplot(221), plt.subplot(222)] plt_topomap(axes=axes, colorbar=False) plt.close('all') plt_topomap(times=[-0.1, 0.2]) plt.close('all') evoked_grad = evoked.copy().crop(0, 0).pick_types(meg='grad') mask = np.zeros((204, 1), bool) mask[[0, 3, 5, 6]] = True names = [] def proc_names(x): names.append(x) return x[4:] evoked_grad.plot_topomap(ch_type='grad', times=[0], mask=mask, show_names=proc_names, **fast_test) assert_equal(sorted(names), ['MEG 011x', 'MEG 012x', 'MEG 013x', 'MEG 014x']) mask = np.zeros_like(evoked.data, dtype=bool) mask[[1, 5], :] = True plt_topomap(ch_type='mag', outlines=None) times = [0.1] plt_topomap(times, ch_type='grad', mask=mask) plt_topomap(times, ch_type='planar1') plt_topomap(times, ch_type='planar2') plt_topomap(times, ch_type='grad', mask=mask, show_names=True, mask_params={'marker': 'x'}) plt.close('all') pytest.raises(ValueError, plt_topomap, times, ch_type='eeg', average=-1e3) pytest.raises(ValueError, plt_topomap, times, ch_type='eeg', average='x') p = plt_topomap(times, ch_type='grad', image_interp='bilinear', show_names=lambda x: x.replace('MEG', '')) subplot = [x for x in p.get_children() if 'Subplot' in str(type(x))] assert len(subplot) >= 1, [type(x) for x in p.get_children()] subplot = subplot[0] have_all = all('MEG' not in x.get_text() for x in subplot.get_children() if isinstance(x, matplotlib.text.Text)) assert have_all # Plot array for ch_type in ('mag', 'grad'): evoked_ = evoked.copy().pick_types(eeg=False, meg=ch_type) plot_topomap(evoked_.data[:, 0], evoked_.info, **fast_test_noscale) # fail with multiple channel types pytest.raises(ValueError, plot_topomap, evoked.data[0, :], evoked.info) # Test title def get_texts(p): return [ x.get_text() for x in p.get_children() if isinstance(x, matplotlib.text.Text) ] p = plt_topomap(times, ch_type='eeg', average=0.01) assert_equal(len(get_texts(p)), 0) p = plt_topomap(times, ch_type='eeg', title='Custom') texts = get_texts(p) assert_equal(len(texts), 1) assert_equal(texts[0], 'Custom') plt.close('all') # delaunay triangulation warning plt_topomap(times, ch_type='mag', layout=None) # projs have already been applied pytest.raises(RuntimeError, plot_evoked_topomap, evoked, 0.1, 'mag', proj='interactive', time_unit='s') # change to no-proj mode evoked = read_evokeds(evoked_fname, 'Left Auditory', baseline=(None, 0), proj=False) fig1 = evoked.plot_topomap('interactive', 'mag', proj='interactive', **fast_test) _fake_click(fig1, fig1.axes[1], (0.5, 0.5)) # click slider data_max = np.max(fig1.axes[0].images[0]._A) fig2 = plt.gcf() _fake_click(fig2, fig2.axes[0], (0.075, 0.775)) # toggle projector # make sure projector gets toggled assert (np.max(fig1.axes[0].images[0]._A) != data_max) pytest.raises(RuntimeError, plot_evoked_topomap, evoked, np.repeat(.1, 50), time_unit='s') pytest.raises(ValueError, plot_evoked_topomap, evoked, [-3e12, 15e6], time_unit='s') for ch in evoked.info['chs']: if ch['coil_type'] == FIFF.FIFFV_COIL_EEG: ch['loc'].fill(0) # Remove extra digitization point, so EEG digitization points # correspond with the EEG electrodes del evoked.info['dig'][85] # Plot skirt evoked.plot_topomap(times, ch_type='eeg', outlines='skirt', **fast_test) # Pass custom outlines without patch eeg_picks = pick_types(evoked.info, meg=False, eeg=True) pos, outlines = _get_pos_outlines(evoked.info, eeg_picks, 0.1) evoked.plot_topomap(times, ch_type='eeg', outlines=outlines, **fast_test) plt.close('all') # Test interactive cmap fig = plot_evoked_topomap(evoked, times=[0., 0.1], ch_type='eeg', cmap=('Reds', True), title='title', **fast_test) fig.canvas.key_press_event('up') fig.canvas.key_press_event(' ') fig.canvas.key_press_event('down') cbar = fig.get_axes()[0].CB # Fake dragging with mouse. ax = cbar.cbar.ax _fake_click(fig, ax, (0.1, 0.1)) _fake_click(fig, ax, (0.1, 0.2), kind='motion') _fake_click(fig, ax, (0.1, 0.3), kind='release') _fake_click(fig, ax, (0.1, 0.1), button=3) _fake_click(fig, ax, (0.1, 0.2), button=3, kind='motion') _fake_click(fig, ax, (0.1, 0.3), kind='release') fig.canvas.scroll_event(0.5, 0.5, -0.5) # scroll down fig.canvas.scroll_event(0.5, 0.5, 0.5) # scroll up plt.close('all') # Pass custom outlines with patch callable def patch(): return Circle((0.5, 0.4687), radius=.46, clip_on=True, transform=plt.gca().transAxes) outlines['patch'] = patch plot_evoked_topomap(evoked, times, ch_type='eeg', outlines=outlines, **fast_test) # Remove digitization points. Now topomap should fail evoked.info['dig'] = None pytest.raises(RuntimeError, plot_evoked_topomap, evoked, times, ch_type='eeg', time_unit='s') plt.close('all') # Error for missing names n_channels = len(pos) data = np.ones(n_channels) pytest.raises(ValueError, plot_topomap, data, pos, show_names=True) # Test error messages for invalid pos parameter pos_1d = np.zeros(n_channels) pos_3d = np.zeros((n_channels, 2, 2)) pytest.raises(ValueError, plot_topomap, data, pos_1d) pytest.raises(ValueError, plot_topomap, data, pos_3d) pytest.raises(ValueError, plot_topomap, data, pos[:3, :]) pos_x = pos[:, :1] pos_xyz = np.c_[pos, np.zeros(n_channels)[:, np.newaxis]] pytest.raises(ValueError, plot_topomap, data, pos_x) pytest.raises(ValueError, plot_topomap, data, pos_xyz) # An #channels x 4 matrix should work though. In this case (x, y, width, # height) is assumed. pos_xywh = np.c_[pos, np.zeros((n_channels, 2))] plot_topomap(data, pos_xywh) plt.close('all') # Test peak finder axes = [plt.subplot(131), plt.subplot(132)] evoked.plot_topomap(times='peaks', axes=axes, **fast_test) plt.close('all') evoked.data = np.zeros(evoked.data.shape) evoked.data[50][1] = 1 assert_array_equal(_find_peaks(evoked, 10), evoked.times[1]) evoked.data[80][100] = 1 assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 100]]) evoked.data[2][95] = 2 assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 95]]) assert_array_equal(_find_peaks(evoked, 1), evoked.times[95]) # Test excluding bads channels evoked_grad.info['bads'] += [evoked_grad.info['ch_names'][0]] orig_bads = evoked_grad.info['bads'] evoked_grad.plot_topomap(ch_type='grad', times=[0], time_unit='ms') assert_array_equal(evoked_grad.info['bads'], orig_bads) plt.close('all')