In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

from mpl_toolkits import mplot3d
from scipy.spatial.distance import squareform, pdist

Load the data

In [2]:
data = pd.read_csv('HCP-MMP1_UniqueRegionList.csv', index_col=0)

data
Out[2]:
regionLongName regionIdLabel LR region Lobe cortex regionID Cortex_ID x-cog y-cog z-cog volmm
regionName
V1_L Primary_Visual_Cortex_L 1_L L V1 Occ Primary_Visual 1 1 100.491589 41.138901 71.637040 6717
MST_L Medial_Superior_Temporal_Area_L 2_L L MST Occ MT+_Complex_and_Neighboring_Visual_Areas 2 5 132.416667 58.901786 82.059524 336
V6_L Sixth_Visual_Area_L 3_L L V6 Occ Dorsal_Stream_Visual 3 3 104.543112 44.481665 103.916749 1009
V2_L Second_Visual_Area_L 4_L L V2 Occ Early_Visual 4 2 102.236656 44.064791 74.401125 6220
V3_L Third_Visual_Area_L 5_L L V3 Occ Early_Visual 5 2 107.926111 40.632159 76.961153 4994
... ... ... ... ... ... ... ... ... ... ... ... ...
STSva_R Area_STSv_anterior_R 376_R R STSva Temp Auditory_Association 376 11 32.924419 117.527907 54.337791 1720
TE1m_R Area_TE1_Middle_R 377_R R TE1m Temp Lateral_Temporal 377 14 26.146605 102.236497 53.246528 2592
PI_R Para-Insular_Area_R 378_R R PI Temp Insular_and_Frontal_Opercular 378 12 47.013363 123.157016 57.926503 898
a32pr_R Area_anterior_32_prime_R 379_R R a32pr Fr Anterior_Cingulate_and_Medial_Prefrontal 379 19 81.723096 153.990326 102.516324 827
p24_R Area_posterior_24_R 380_R R p24 Fr Anterior_Cingulate_and_Medial_Prefrontal 380 19 86.096222 161.202423 88.541696 1403

360 rows 12 columns

Plotting

Simply plot all the coordinates first

In [3]:
fig = plt.figure()

ax = plt.axes(projection='3d')

ax.scatter3D(data['x-cog'],
             data['y-cog'],
             data['z-cog']);

Create masks for the left and the right hemispheres

In [4]:
data[data.index.str.contains('_L')]
Out[4]:
regionLongName regionIdLabel LR region Lobe cortex regionID Cortex_ID x-cog y-cog z-cog volmm
regionName
V1_L Primary_Visual_Cortex_L 1_L L V1 Occ Primary_Visual 1 1 100.491589 41.138901 71.637040 6717
MST_L Medial_Superior_Temporal_Area_L 2_L L MST Occ MT+_Complex_and_Neighboring_Visual_Areas 2 5 132.416667 58.901786 82.059524 336
V6_L Sixth_Visual_Area_L 3_L L V6 Occ Dorsal_Stream_Visual 3 3 104.543112 44.481665 103.916749 1009
V2_L Second_Visual_Area_L 4_L L V2 Occ Early_Visual 4 2 102.236656 44.064791 74.401125 6220
V3_L Third_Visual_Area_L 5_L L V3 Occ Early_Visual 5 2 107.926111 40.632159 76.961153 4994
... ... ... ... ... ... ... ... ... ... ... ... ...
STSva_L Area_STSv_anterior_L 176_L L STSva Temp Auditory_Association 176 11 142.689243 118.134462 52.832669 1004
TE1m_L Area_TE1_Middle_L 177_L L TE1m Temp Lateral_Temporal 177 14 155.554922 101.395287 54.901178 2631
PI_L Para-Insular_Area_L 178_L L PI Temp Insular_and_Frontal_Opercular 178 12 134.650549 124.293407 56.102198 910
a32pr_L Area_anterior_32_prime_L 179_L L a32pr Fr Anterior_Cingulate_and_Medial_Prefrontal 179 19 97.782313 155.883735 101.539270 1617
p24_L Area_posterior_24_L 180_L L p24 Fr Anterior_Cingulate_and_Medial_Prefrontal 180 19 95.442887 162.526279 84.888577 1427

180 rows 12 columns

In [5]:
left_mask, right_mask = [data.index.str.contains(hemisphere) for hemisphere in ('_L', '_R')]
In [6]:
print('Left mask')
data[left_mask][:3]
Left mask
Out[6]:
regionLongName regionIdLabel LR region Lobe cortex regionID Cortex_ID x-cog y-cog z-cog volmm
regionName
V1_L Primary_Visual_Cortex_L 1_L L V1 Occ Primary_Visual 1 1 100.491589 41.138901 71.637040 6717
MST_L Medial_Superior_Temporal_Area_L 2_L L MST Occ MT+_Complex_and_Neighboring_Visual_Areas 2 5 132.416667 58.901786 82.059524 336
V6_L Sixth_Visual_Area_L 3_L L V6 Occ Dorsal_Stream_Visual 3 3 104.543112 44.481665 103.916749 1009
In [7]:
print('Right mask')
data[right_mask][:3]
Right mask
Out[7]:
regionLongName regionIdLabel LR region Lobe cortex regionID Cortex_ID x-cog y-cog z-cog volmm
regionName
V1_R Primary_Visual_Cortex_R 201_R R V1 Occ Primary_Visual 201 1 78.060375 44.539286 74.333474 7089
MST_R Medial_Superior_Temporal_Area_R 202_R R MST Occ MT+_Complex_and_Neighboring_Visual_Areas 202 5 43.620295 63.751227 78.013093 611
V6_R Sixth_Visual_Area_R 203_R R V6 Occ Dorsal_Stream_Visual 203 3 72.226868 49.256228 102.929715 1124

Plotting with masks

In [8]:
fig = plt.figure()

ax = plt.axes(projection='3d')

plt.title('Left hemisphere: red\nRight hemisphere: green')

ax.scatter3D(data['x-cog'][left_mask],
             data['y-cog'][left_mask],
             data['z-cog'][left_mask],
             c=data['z-cog'][left_mask],
             cmap="Reds");

ax.scatter3D(data['x-cog'][right_mask],
             data['y-cog'][right_mask],
             data['z-cog'][right_mask],
             c=data['z-cog'][right_mask],
             cmap="Greens");

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');

Plot labels

Preview one of the labels:

In [9]:
data.loc['V1_L']
Out[9]:
regionLongName    Primary_Visual_Cortex_L
regionIdLabel                         1_L
LR                                      L
region                                 V1
Lobe                                  Occ
cortex                     Primary_Visual
regionID                                1
Cortex_ID                               1
x-cog                             100.492
y-cog                             41.1389
z-cog                              71.637
volmm                                6717
Name: V1_L, dtype: object

Plot with selected labels

For text annotation examples see: https://matplotlib.org/stable/gallery/mplot3d/text3d.html

In [10]:
labels = ['V1_L', 'V1_R', '1_L', '1_R', 'TGv_L', 'TGv_R', '10pp_L', '10pp_R']

fig = plt.figure()

ax = plt.axes(projection='3d')
ax.view_init(elev=20., azim=-45)

plt.title('Left hemisphere: red\nRight hemisphere: green')

ax.scatter3D(data['x-cog'][left_mask],
             data['y-cog'][left_mask],
             data['z-cog'][left_mask],
             c=data['z-cog'][left_mask],
             cmap="Reds");

ax.scatter3D(data['x-cog'][right_mask],
             data['y-cog'][right_mask],
             data['z-cog'][right_mask],
             c=data['z-cog'][right_mask],
             cmap="Greens");

# Display labels (texts) to see which ROI is where.
# None is direction of the text. None is en face.
for label in labels:
    region = data.loc[label]
    ax.text(region['x-cog'],
            region['y-cog'],
            region['z-cog']*1.2,
            label,
            None,
            color='blue')
    
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');

It seems that this is presented in radiology notation, i.e., where left is right and right is left. It doesn't really matter here, as we are primarily interested in the distances between the regions.

Calculate the distances

Preview all the columns with coordinates.

In [11]:
data[['x-cog', 'y-cog', 'z-cog']]
Out[11]:
x-cog y-cog z-cog
regionName
V1_L 100.491589 41.138901 71.637040
MST_L 132.416667 58.901786 82.059524
V6_L 104.543112 44.481665 103.916749
V2_L 102.236656 44.064791 74.401125
V3_L 107.926111 40.632159 76.961153
... ... ... ...
STSva_R 32.924419 117.527907 54.337791
TE1m_R 26.146605 102.236497 53.246528
PI_R 47.013363 123.157016 57.926503
a32pr_R 81.723096 153.990326 102.516324
p24_R 86.096222 161.202423 88.541696

360 rows 3 columns

Prepare an array and data frame with distances

In [12]:
distances_array = squareform(
                                pdist(X=data[['x-cog', 'y-cog', 'z-cog']],
                                      metric='euclidean')
                            )

distances_df = pd.DataFrame(distances_array, index=data.index, columns=data.index)

distances_df
Out[12]:
regionName V1_L MST_L V6_L V2_L V3_L V4_L V8_L 4_L 3b_L FEF_L ... p47r_R TGv_R MBelt_R LBelt_R A4_R STSva_R TE1m_R PI_R a32pr_R p24_R
regionName
V1_L 0.000000 37.991563 32.704258 4.387056 9.158334 20.061667 29.055507 86.733295 86.130607 97.955835 ... 142.831889 104.079605 90.618716 84.999718 103.117813 103.440160 97.970934 98.867946 118.495699 122.099317
MST_L 37.991563 0.000000 38.244127 34.490876 30.976765 24.437924 27.886706 65.801588 60.889074 71.981875 ... 144.522304 114.478689 103.425309 100.283248 119.382344 118.761206 118.327562 109.566699 109.682037 112.485655
V6_L 32.704258 38.244127 0.000000 29.608539 27.438432 37.646137 51.185146 66.763480 66.905377 81.480353 ... 146.433147 121.144079 95.161823 87.986215 107.337188 113.679654 109.768412 107.770987 111.869840 119.165496
V2_L 4.387056 34.490876 29.608539 0.000000 7.120857 18.974758 28.358150 82.434470 81.759962 93.647951 ... 141.002123 103.811014 89.427881 83.906642 102.341352 102.973555 98.087513 97.860131 115.303486 119.086919
V3_L 9.158334 30.976765 27.438432 7.120857 0.000000 14.395290 27.614239 82.496483 81.100872 93.516854 ... 146.683721 110.188978 95.786052 90.223474 108.863362 109.772612 105.096934 104.321775 119.120690 123.076568
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
STSva_R 103.440160 118.761206 113.679654 102.973555 109.772612 117.107303 111.489147 113.323729 119.878821 120.418072 ... 57.157519 37.725809 23.995358 32.401890 24.107932 0.000000 16.761767 15.590512 77.665886 76.841467
TE1m_R 97.970934 118.327562 109.768412 98.087513 105.096934 113.517170 109.224408 118.676658 125.212484 127.749421 ... 73.528070 46.346724 29.544628 32.112512 25.180933 16.761767 0.000000 29.916415 90.524682 91.195866
PI_R 98.867946 109.566699 107.770987 97.860131 104.321775 110.614657 104.191842 101.325689 107.475675 106.937746 ... 49.870358 33.698920 20.719343 32.023794 30.632640 15.590512 29.916415 0.000000 64.371661 62.547685
a32pr_R 118.495699 109.682037 111.869840 115.303486 119.120690 123.497117 119.258244 65.957407 72.939670 64.797134 ... 55.639579 86.394624 63.100827 68.997981 74.940281 77.665886 90.524682 64.371661 0.000000 16.322647
p24_R 122.099317 112.485655 119.165496 119.086919 123.076568 126.070447 119.672959 75.247200 80.564816 71.202062 ... 48.871635 78.992597 66.467288 74.393928 79.102505 76.841467 91.195866 62.547685 16.322647 0.000000

360 rows 360 columns

Visualize the distances

In [13]:
# Prepare a bigger figure.
# Axes are unequal because colorbar will be smaller and
# seaborn doesn't compensate for that automatically.
plt.figure(figsize=(16, 13))

# Color bar keyworded arguments are passed so that the colorbar
# will be smaller -- this way the visualization looks better.
ax = sns.heatmap(distances_df,
                 cbar_kws={"shrink": 0.5})

ax.set_xlabel('Region name')
ax.set_ylabel('Region name');