Structural poperties of the MEG-informed parcellationsΒΆ

Import the required packages

import os.path as op
import numpy as np
import pickle

import matplotlib.pyplot as plt
import matplotlib.patches as patches

from mne import (read_forward_solution, pick_types_forward,
                 convert_forward_solution, read_labels_from_annot,
                 spatial_src_adjacency, read_source_spaces)
from mne.viz import get_brain_class
from mne.datasets import sample

import megicparc

Define input parameters for the flame algorithm running in megicperc

gamma_tot = np.arange(0, 1.01, 0.2)
knn_tot = [10, 20, 30, 40]
theta = 0.05
parc = 'aparc'
sensors_meg = 'grad'

folder_fl = op.join('..', 'data', 'data_mne_sample')
string_target_file = op.join(folder_fl,
                        '{:s}_flame_grad_k{:d}_gamma{:1.2f}_theta{:1.2f}.pkl')

Load lead-field matrix, source-space and anatomy-based Atlas

data_path = sample.data_path()
subjects_dir = op.join(data_path, 'subjects')
subject = 'sample'
fwd_file = op.join(data_path, 'MEG', subject, 'sample_audvis-meg-eeg-oct-6-fwd.fif')
src_file = op.join(folder_fl, 'source_space_distance-src.fif')

fwd = read_forward_solution(fwd_file)
fwd = pick_types_forward(fwd, meg=sensors_meg, eeg=False,
                         ref_meg=False, exclude='bads')
fwd = convert_forward_solution(fwd, surf_ori=True, force_fixed=True,
                               use_cps=True)
src = read_source_spaces(src_file)
fwd['src'] = src
n_vert = fwd['src'][0]['nuse'] + fwd['src'][1]['nuse']

label_lh = read_labels_from_annot(subject=subject, parc=parc, hemi='lh',
                                  subjects_dir=subjects_dir)
label_rh = read_labels_from_annot(subject=subject, parc=parc, hemi='rh',
                                  subjects_dir=subjects_dir)
label = label_lh + label_rh
Reading forward solution from /u/29/sommars1/unix/mne_data/MNE-sample-data/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif...
    Reading a source space...
    Computing patch statistics...
    Patch information added...
    Distance information added...
    [done]
    Reading a source space...
    Computing patch statistics...
    Patch information added...
    Distance information added...
    [done]
    2 source spaces read
    Desired named matrix (kind = 3523) not available
    Read MEG forward solution (7498 sources, 306 channels, free orientations)
    Desired named matrix (kind = 3523) not available
    Read EEG forward solution (7498 sources, 60 channels, free orientations)
    Forward solutions combined: MEG, EEG
    Source spaces transformed to the forward solution coordinate frame
    203 out of 366 channels remain after picking
    Average patch normals will be employed in the rotation to the local surface coordinates....
    Converting to surface-based source orientations...
    [done]
    Reading a source space...
    Computing patch statistics...
    Patch information added...
    Distance information added...
    [done]
    Reading a source space...
    Computing patch statistics...
    Patch information added...
    Distance information added...
    [done]
    2 source spaces read
Reading labels from parcellation...
   read 34 labels from /u/29/sommars1/unix/mne_data/MNE-sample-data/subjects/sample/label/lh.aparc.annot
Reading labels from parcellation...
   read 34 labels from /u/29/sommars1/unix/mne_data/MNE-sample-data/subjects/sample/label/rh.aparc.annot

Initialization

num_roi = np.zeros((np.size(knn_tot), np.size(gamma_tot)))
n_cc = {x : [] for x in \
        ['k%d_gamma%1.2f'%(knn, gamma) for knn in knn_tot for gamma in gamma_tot]}
coverage = np.zeros((np.size(knn_tot), np.size(gamma_tot)))

Analysis with anatomy-based parcels.

proj_anrois = megicparc.labels_to_array(label, fwd['src'])

#   Number of regions
if proj_anrois['outliers'] > 0:
    aux_num_roi = len(proj_anrois['parcel']) - 1
else:
    aux_num_roi = len(proj_anrois['parcel'])
num_roi_an = aux_num_roi
#   Coverage
coverage_an = 100 * (n_vert - proj_anrois['outliers']) / n_vert

Analysis with meg-informed parcels

bmesh_adj_mat = np.array(spatial_src_adjacency(fwd['src']).todense())

for idx_g, gamma in enumerate(gamma_tot):
    for idx_k, knn in enumerate(knn_tot):

        target_file = string_target_file.format(
                        subject, knn, gamma, theta)
        print('Loading %s'%target_file)
        with open(target_file, 'rb') as aux_lf:
            flame_data = pickle.load(aux_lf)

            # Number of regions
            num_roi[idx_k, idx_g] = flame_data['centroids']
            # Coverage
            coverage[idx_k, idx_g] = \
                    100 * (n_vert - flame_data['outliers']) / n_vert
            # Number of connected components
            n_cc['k%d_gamma%1.2f'%(knn, gamma)] += \
                 [len(megicparc.compute_connected_components(
                         bmesh_adj_mat, flame_data['parcel'][ir])) \
                         for ir in range(flame_data['centroids'])]
-- number of adjacent vertices : 8196
/m/home/home2/29/sommars1/data/Documents/megicparc/examples/plot_3_structural_properties.py:86: RuntimeWarning: 8.5% of original source space vertices have been omitted, tri-based adjacency will have holes.
Consider using distance-based adjacency or morphing data to all source space vertices.
  bmesh_adj_mat = np.array(spatial_src_adjacency(fwd['src']).todense())
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma0.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma0.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma0.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma0.20_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma0.20_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.20_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma0.20_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma0.40_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma0.40_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.40_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma0.40_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma0.60_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma0.60_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.60_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma0.60_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma0.80_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma0.80_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.80_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma0.80_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k10_gamma1.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k20_gamma1.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma1.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k40_gamma1.00_theta0.05.pkl

Compute mean and SEM of the number of connected components over the regions of a MEG-informed parcellations

n_cc_ave = np.zeros((np.size(knn_tot), np.size(gamma_tot)))
n_cc_sem = np.zeros((np.size(knn_tot), np.size(gamma_tot)))

for idx_g, gamma in enumerate(gamma_tot):
    for idx_k, knn in enumerate(knn_tot):
        aux_cc = np.asanyarray(n_cc['k%d_gamma%1.2f'%(knn, gamma)])
        n_cc_ave[idx_k, idx_g] = np.mean(aux_cc)
        n_cc_sem[idx_k, idx_g] = np.std(aux_cc, ddof=1) / np.sqrt(np.size(aux_cc))

Additional analysis: impact of the anatomical constraints

k_nn_ac = [30]
theta_ac = [0, 0.05]
gamma_ac = np.arange(1, 0.4, -0.2)

tolls = [0.05]

spread = {}
num_roi_constr = {}

for idx_t, theta in enumerate(theta_ac):
    for idx_k, knn in enumerate(k_nn_ac):
        for idx_g, gamma in enumerate(gamma_ac):
#           6.1. Reload parcellation
            target_file = string_target_file.format(subject, knn, gamma,
                                                    theta)
            print('Loading %s'%target_file)
            with open(target_file, 'rb') as aux_lf:
                flame_data = pickle.load(aux_lf)

            num_roi_constr['k%d_gamma%1.2f_theta%1.2f'%(knn, gamma, theta)] = \
                flame_data['centroids']
#           6.2. Compute cotingency matrix
            conting_mat = np.array([
                    np.array([
                    np.intersect1d(p_fl, p_an).shape[0]
                    for p_an in proj_anrois['parcel']])
                    for p_fl in flame_data['parcel'][0:flame_data['centroids']]])
            conting_mat = conting_mat / \
                    np.sum(conting_mat, axis=1)[:, np.newaxis]
#           6.3. Compute spread of flame regions over anatomical
            spread['k%d_gamma%1.2f_theta%1.2f'%(knn, gamma, theta)] = \
                np.zeros((flame_data['centroids'], np.size(tolls)))

            for idx_t, toll in enumerate(tolls):
                spread['k%d_gamma%1.2f_theta%1.2f'%(knn, gamma, theta)] [:, idx_t] = \
                    np.sum(conting_mat > toll, axis=1)
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma1.00_theta0.00.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.80_theta0.00.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.60_theta0.00.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma1.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.80_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.60_theta0.05.pkl

General parameters for plotting

plt.rc('text', usetex=True)
plt.rc('font', family='serif', size=22)
plt.rcParams['axes.grid'] = True
plt.rcParams['lines.linewidth'] = 3
plt.rcParams['errorbar.capsize'] = 3.5
plt.rcParams["figure.figsize"] = [12, 8]
plt.rcParams['patch.force_edgecolor'] = True

colors = np.array([np.array([0, 1, 0]),
                  np.array([0, 0, 1]),
                  np.array([1, 0, 0]),
                  np.array([0, 0.9655, 1])])

Plot 1. Number of regions

f_numreg = plt.figure()
for idx_k, knn in enumerate(knn_tot):
    plt.plot(gamma_tot, num_roi[idx_k,], color=colors[idx_k],
                 label='$k$ = %d'%knn)
plt.plot(gamma_tot, num_roi_an*np.ones(np.size(gamma_tot)),
             'k--', label='DK')
plt.legend(loc=[0.68, 0.47], fontsize=18)
plt.xlabel(r'Weight of the spatial distances', fontsize=25)
plt.ylabel(r'Number of parcels', fontsize=25)
plt.ylim(0, 350)
plt.xlim(-0.05, 1.05)

f_numreg.set_size_inches(8.5, 6)
plt.tight_layout(pad=1.5)
plot 3 structural properties

Plot 2. Number of connected components

f_cc = plt.figure()
for idx_k, knn in enumerate(knn_tot):
    plt.errorbar(gamma_tot, n_cc_ave[idx_k, :], yerr=n_cc_sem[idx_k, :],
                 label='$k$ = %d'%knn, color=colors[idx_k])
plt.legend(fontsize=18)
plt.xlabel(r'Weight of the spatial distances', fontsize=25)
plt.ylabel(r'Number connected components', fontsize=25)
plt.ylim(0, 30)
plt.xlim(-0.05, 1.05)
#plt.show()

f_cc.set_size_inches(8.5, 6)
plt.tight_layout(pad=1.5)
plot 3 structural properties

. Plot 3. (Additional) Impact of anatomical constraints

bins = np.arange(-0.5, 69, 1)
alpha = 0.7

knn = k_nn_ac[0]
toll = tolls[0]

fac, axac = plt.subplots(1, np.size(gamma_ac), figsize=[13, 5])
#fac.suptitle('$k =$ %d'%knn)
idx_t = 0

for idx_g, gamma in enumerate(gamma_ac):
    aux_th1 = spread['k%d_gamma%1.2f_theta%1.2f'%(knn, gamma, theta_ac[1])][:, idx_t]
    axac[idx_g].hist(aux_th1, bins=bins, alpha=alpha,
                    weights=np.ones_like(aux_th1)/np.shape(aux_th1)[0]*100,
                    label=r'$\theta = $ %1.2f'%theta_ac[1])
    aux_th2 = spread['k%d_gamma%1.2f_theta%1.2f'%(knn, gamma, theta_ac[0])] [:, idx_t]
    axac[idx_g].hist(aux_th2, bins=bins, alpha=alpha,
                    weights=np.ones_like(aux_th2)/np.shape(aux_th2)[0]*100,
                    label=r'$\theta = $ %1.2f'%theta_ac[0])
    axac[idx_g].set_ylim(0, 100)
    axac[idx_g].set_xlim(-0.5, 10.5)
    #axac[idx_t, idx_g].legend()

    if idx_t == 0:
        axac[idx_g].set_title(r'$\gamma = %1.1f$ '%(gamma))
        axac[idx_g].set_xticks(np.arange(1, 11, 2))
        axac[idx_g].text(6.5, 85, '%d ;'%(num_roi_constr['k%d_gamma%1.2f_theta0.05'%(knn, gamma)]),
                         ha='center', color='dodgerblue', fontweight='bold')
        axac[idx_g].text(8.5, 85, '%d'%(num_roi_constr['k%d_gamma%1.2f_theta0.00'%(knn, gamma)]),
                         ha='center', color='darkorange', fontweight='bold')
        axac[idx_g].text(7.2, 75, 'parcels', ha='center')
        rect = patches.FancyBboxPatch((5, 72), 4.5, 22,
                                  boxstyle='round', facecolor='white',
                                 edgecolor='lightgray', zorder=2)
        axac[idx_g].add_patch(rect)
    else:
        axac[idx_g].set_xticks(np.arange(1, 11, 2))

    if idx_g == 0:
        axac[idx_g].set_ylabel(
            r'Number of parcels ($\%$)',
                         fontsize=30)
    else:
        axac[idx_g].set_yticklabels([])

axac[-1].legend(bbox_to_anchor=(1, 0.5), loc='center left',
            borderaxespad=0.2)
fac.text(0.45, 0.02, 'Number of intersecting atlas parcels', ha='center',
          fontsize=30)
plt.tight_layout(pad=1.5)


gamma_tplot = np.arange(0, 1, 0.1)
theta_tplot = np.array([0.01, 0.025, 0.05, 0.1, 0.2])
f_tg = np.array([theta * (gamma_tplot / (1-gamma_tplot)) \
             for theta in theta_tplot])

colors_tplot = np.array([np.array([0, 1, 0]),
        np.array([0, 0, 1]), np.array([1, 0, 0]),
        np.array([0, 0.9655, 1]), np.array([1, 0.4828, 0.8621])])

f_tplot = plt.figure()
for idx_t, theta in enumerate(theta_tplot):
    plt.plot(gamma_tplot, f_tg[idx_t],
         label=r'$\theta$ = %1.3f'%theta, color=colors_tplot[idx_t])
plt.legend()
plt.xlim([0, 0.9])
plt.ylim([0, 2])
plt.xlabel(r'$\gamma$', fontsize=30)
plt.ylabel(r'$\theta$ $\frac{\gamma}{1-\gamma}$', fontsize=30)

f_tplot.set_size_inches(8, 6.5)
  • $\gamma = 1.0$ , $\gamma = 0.8$ , $\gamma = 0.6$
  • plot 3 structural properties

Plot 5. Spatial cohesion

Brain = get_brain_class()

knn_plot = 30
gamma_plot = [0, 0.4, 0.6, 1]
theta_plot = 0.05
cart_coord = [-0.03, 0.025, 0.08]

src = fwd['src']
V = np.concatenate((src[0]['rr'][src[0]['vertno']],
               src[1]['rr'][src[1]['vertno']]), axis=0)
nvert = src[0]['nuse'] + src[1]['nuse']

idx_vert = np.argmin(np.linalg.norm(V - cart_coord, axis=1))

for gamma in gamma_plot:
    target_file = string_target_file.format(
                        subject, knn_plot, gamma, theta_plot)
    print('Loading %s'%target_file)
    with open(target_file, 'rb') as aux_lf:
        flame_data = pickle.load(aux_lf)
    flame_labels = megicparc.store_flame_labels(flame_data, src, subject)

    parcels_vector = np.zeros(nvert, dtype='int') - 1
    for ir in range(flame_data['centroids']):
        parcels_vector[flame_data['parcel'][ir]] = ir
    sel_roi = parcels_vector[idx_vert]
    index = [sel_roi + 1]

    brain = megicparc.plot_flame_labels(index, flame_labels, src, subject,
            subjects_dir, surf='inflated', color = [1, 0.64, 0.],
            plot_region=True, plot_points=False, plot_borders=False)
    brain.show_view(azimuth=173, elevation= 60)
  • plot 3 structural properties
  • plot 3 structural properties
  • plot 3 structural properties
  • plot 3 structural properties
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.00_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.40_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma0.60_theta0.05.pkl
Loading ../data/data_mne_sample/sample_flame_grad_k30_gamma1.00_theta0.05.pkl

Plot 7. Reduced source space on top of DK atlas

brain_redV = Brain(subject, hemi='both', surf='inflated',
    background='white', subjects_dir=subjects_dir, alpha=1)
#   Superimpose anatomical regions
for ir in range(len(label)):
    brain_redV.add_label(label[ir], hemi=label[ir].hemi, alpha=0.9)

#  Superimpose centroids
megicparc.plot_flame_centroids(flame_data, src, subject, subjects_dir,
                           brain_redV)

brain_redV.show_view(azimuth=170, elevation=90)

""
plot 3 structural properties
''

Total running time of the script: (1 minutes 38.736 seconds)

Gallery generated by Sphinx-Gallery