Exemple #1
0
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')
Exemple #2
0
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')