Spatio-temporal sensor-space statistics (EEG)ΒΆ

Run a non-parametric spatio-temporal cluster stats on EEG sensors on the contrast faces vs. scrambled.

import os.path as op
import sys

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

import mne
from mne.stats import permutation_cluster_1samp_test
from mne.viz import plot_topomap

sys.path.append(op.join('..', '..', 'processing'))
from library.config import (meg_dir, l_freq, exclude_subjects, annot_kwargs,
                            set_matplotlib_defaults, random_state)  # noqa: E402

Read all the data

contrasts = list()

for subject_id in range(1, 20):
    if subject_id in exclude_subjects:
        continue
    subject = "sub%03d" % subject_id
    print("processing subject: %s" % subject)
    data_path = op.join(meg_dir, subject)
    contrast = mne.read_evokeds(op.join(data_path, '%s_highpass-%sHz-ave.fif'
                                        % (subject, l_freq)),
                                condition='contrast')
    contrast.pick_types(meg=False, eeg=True).crop(None, 0.8)
    contrast.apply_baseline((-0.2, 0.0))
    contrasts.append(contrast)

contrast = mne.combine_evoked(contrasts, 'equal')

Out:

processing subject: sub002
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub002/sub002_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 155 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub003
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub003/sub003_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 146 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub004
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub004/sub004_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 139 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub006
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub006/sub006_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 108 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub007
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub007/sub007_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 193 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub008
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub008/sub008_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 127 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub009
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub009/sub009_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 89 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub010
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub010/sub010_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 115 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub011
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub011/sub011_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 113 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub012
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub012/sub012_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 116 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub013
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub013/sub013_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 120 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub014
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub014/sub014_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 116 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub015
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub015/sub015_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 60 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub017
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub017/sub017_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 89 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub018
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub018/sub018_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 135 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)
processing subject: sub019
Reading /tsi/doctorants/data_gramfort/dgw_faces_reproduce/MEG/sub019/sub019_highpass-NoneHz-ave.fif ...
    Read a total of 1 projection items:
        Average EEG reference (1 x 70) active
    Found the data of interest:
        t =    -200.00 ...    2900.00 ms (contrast)
        0 CTF compensation matrices available
        nave = 138 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Applying baseline correction (mode: mean)

Assemble the data and run the cluster stats on channel data

data = np.array([c.data for c in contrasts])

connectivity = None
tail = 0.  # for two sided test

# set cluster threshold
p_thresh = 0.01 / (1 + (tail == 0))
n_samples = len(data)
threshold = -stats.t.ppf(p_thresh, n_samples - 1)
if np.sign(tail) < 0:
    threshold = -threshold

# Make a triangulation between EEG channels locations to
# use as connectivity for cluster level stat
connectivity = mne.channels.find_ch_connectivity(contrast.info, 'eeg')[0]

data = np.transpose(data, (0, 2, 1))  # transpose for clustering

cluster_stats = permutation_cluster_1samp_test(
    data, threshold=threshold, n_jobs=2, verbose=True, tail=1,
    connectivity=connectivity, out_type='indices',
    check_disjoint=True, step_down_p=0.05, seed=random_state)

T_obs, clusters, p_values, _ = cluster_stats
good_cluster_inds = np.where(p_values < 0.05)[0]

print("Good clusters: %s" % good_cluster_inds)

Out:

Could not find a connectivity matrix for the data. Computing connectivity based on Delaunay triangulations.
-- number of connected vertices : 70
stat_fun(H1): min=-9.714884 max=12.075012
No disjoint connectivity sets found
Running initial clustering
Found 8 clusters
Permuting 1023 times...
Computing cluster p-values
Step-down-in-jumps iteration #1 found 1 cluster to exclude from subsequent iterations
Permuting 1023 times...
Computing cluster p-values
Step-down-in-jumps iteration #2 found 0 additional clusters to exclude from subsequent iterations
Done.
Good clusters: [7]

Visualize the spatio-temporal clusters

set_matplotlib_defaults()
times = contrast.times * 1e3
colors = 'r', 'steelblue'
linestyles = '-', '--'

pos = mne.find_layout(contrast.info).pos

T_obs_max = 5.
T_obs_min = -T_obs_max

# loop over significant clusters
for i_clu, clu_idx in enumerate(good_cluster_inds):

    # unpack cluster information, get unique indices
    time_inds, space_inds = np.squeeze(clusters[clu_idx])
    ch_inds = np.unique(space_inds)
    time_inds = np.unique(time_inds)

    # get topography for T0 stat
    T_obs_map = T_obs[time_inds, ...].mean(axis=0)

    # get signals at significant sensors
    signals = data[..., ch_inds].mean(axis=-1)
    sig_times = times[time_inds]

    # create spatial mask
    mask = np.zeros((T_obs_map.shape[0], 1), dtype=bool)
    mask[ch_inds, :] = True

    # initialize figure
    fig, ax_topo = plt.subplots(1, 1, figsize=(7, 2.))

    # plot average test statistic and mark significant sensors
    image, _ = plot_topomap(T_obs_map, pos, mask=mask, axes=ax_topo,
                            vmin=T_obs_min, vmax=T_obs_max,
                            show=False)

    # advanced matplotlib for showing image with figure and colorbar
    # in one plot
    divider = make_axes_locatable(ax_topo)

    # add axes for colorbar
    ax_colorbar = divider.append_axes('right', size='5%', pad=0.05)
    plt.colorbar(image, cax=ax_colorbar, format='%0.1f')
    ax_topo.set_xlabel('Averaged t-map\n({:0.1f} - {:0.1f} ms)'.format(
        *sig_times[[0, -1]]
    ))
    ax_topo.annotate(chr(65 + 2 * i_clu), (0.1, 1.1), **annot_kwargs)

    # add new axis for time courses and plot time courses
    ax_signals = divider.append_axes('right', size='300%', pad=1.2)
    for signal, name, col, ls in zip(signals, ['Contrast'], colors,
                                     linestyles):
        ax_signals.plot(times, signal * 1e6, color=col,
                        linestyle=ls, label=name)

    # add information
    ax_signals.axvline(0, color='k', linestyle=':', label='stimulus onset')
    ax_signals.set_xlim([times[0], times[-1]])
    ax_signals.set_xlabel('Time [ms]')
    ax_signals.set_ylabel('Amplitude [uV]')

    # plot significant time range
    ymin, ymax = ax_signals.get_ylim()
    ax_signals.fill_betweenx((ymin, ymax), sig_times[0], sig_times[-1],
                             color='orange', alpha=0.3)
    ax_signals.legend(loc='lower right')
    title = 'Cluster #{0} (p < {1:0.3f})'.format(i_clu + 1, p_values[clu_idx])
    ax_signals.set(ylim=[ymin, ymax], title=title)
    ax_signals.annotate(chr(65 + 2 * i_clu + 1), (-0.125, 1.1), **annot_kwargs)

    # clean up viz
    fig.tight_layout(pad=0.5, w_pad=0)
    fig.subplots_adjust(bottom=.05)
    plt.savefig(op.join('..', 'figures',
                        'spatiotemporal_stats_cluster_highpass-%sHz-%02d.pdf'
                        % (l_freq, i_clu)))
    plt.show()
../../_images/sphx_glr_plot_sensor_spatio_temporal_cluster_stats_001.png

Total running time of the script: ( 0 minutes 15.428 seconds)

Gallery generated by Sphinx-Gallery