Python tutorial¶
subcortex_visualization is an open-source Python package for programmatically visualizing region-level data across twelve popular atlases for the subcortex (including thalamic nuclei and the brainstem) and cerebellum. Visualizations are rendered as resolution-independent two-dimensional vector graphics, inspired by the ggseg R package for cortical data, with standardized rendering conventions that make results directly comparable across atlases.
This tutorial covers the core functionality of the package:
- Exploring the built-in atlases — viewing the twelve included atlases with default and custom colormaps
- Choosing view angles — displaying medial, lateral, superior, and inferior views
- Controlling transparency — global alpha and significance-based transparency
- Working with your own data — formatting and plotting empirical region-level data
- Extracting regional statistics from volumetric data — using
parcel_segstatsto parcellate NIfTI images - Combining cortical and subcortical visualizations — integrating with
neuromapsfor whole-brain figures
An equivalent R package, subcortexVisualizationR, provides the same functionality and atlases with a nearly identical interface.
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import os
from subcortex_visualization.plotting import plot_subcortical_data
from subcortex_visualization.utils import get_atlas_regions
from subcortex_visualization import segmentation, plotting
1. Exploring the built-in atlases¶
The package includes twelve pre-vectorized subcortical and cerebellar atlases that serve as scaffolds for visualizing region-level summary statistics:
| Atlas | Regions |
|---|---|
aseg_subcortex |
FreeSurfer aseg: accumbens, amygdala, caudate, hippocampus, pallidum, putamen, thalamus |
Melbourne_S1 through Melbourne_S4 |
Melbourne Subcortex Atlas at four resolutions (8–27 subcortical regions per hemisphere) |
AICHA_subcortex |
AICHA subcortical atlas (20 regions per hemisphere) |
Brainnetome_subcortex |
Brainnetome subcortical atlas (18 regions per hemisphere) |
CIT168_subcortex |
CIT168 reinforcement learning atlas (14 regions per hemisphere) |
Thalamus_HCP |
HCP-based thalamic nuclei (7 nuclei per hemisphere) |
Thalamus_THOMAS |
THOMAS thalamic nuclei atlas (11 nuclei per hemisphere) |
Brainstem_Navigator |
Brainstem Navigator atlas (32 bilateral regions per hemisphere + 5 midline regions) |
SUIT_cerebellar_lobule |
SUIT cerebellar lobule atlas (17 unique regions; 10 per hemisphere, 7 midline vermis regions) |
plot_subcortical_data¶
The central function is plot_subcortical_data. With default parameters, it plots one color per region using the viridis colormap for the left hemisphere of the aseg subcortex atlas.
More information about function arguments can be found in the API reference for Python and R.
from subcortex_visualization.plotting import plot_subcortical_data
plot_subcortical_data(fill_title="Demonstrating default parameters")
We can easily swap out the atlas for any of the other eleven. Here we plot the AICHA subcortex atlas with the plasma colormap and the Brainnetome subcortex atlas with the hsv colormap, both for both hemispheres:
from subcortex_visualization.plotting import plot_subcortical_data
# A. AICHA subcortex atlas, plasma colormap
plot_subcortical_data(atlas="AICHA_subcortex", cmap="plasma", hemisphere='both',
fill_title="AICHA with plasma colormap")
# B. Brainnetome subcortex atlas, rainbow colormap
plot_subcortical_data(atlas="Brainnetome_subcortex", cmap="hsv", hemisphere='both',
fill_title="Brainnetome subcortex with rainbow colormap")
2. Choosing view angles¶
For each atlas, medial, lateral, superior, and inferior views are available for both hemispheres, either separately or combined. By default, plot_subcortical_data shows medial and lateral views. You can specify any combination with the views argument.
The one exception is the SUIT cerebellar lobule atlas, which is based on a two-dimensional flatmap representation of the cerebellum and is not designed to be shown in three-dimensional views.
Here we demonstrate all four views with the THOMAS thalamic nuclei atlas and the Brainstem Navigator atlas:
from subcortex_visualization.plotting import plot_subcortical_data
from matplotlib import colors as mcolors
# A. THOMAS thalamic nuclei atlas
THOMAS_cmap = mcolors.LinearSegmentedColormap.from_list("", ["white", "#d14662"])
plot_subcortical_data(atlas="Thalamus_THOMAS", cmap=THOMAS_cmap, hemisphere='both',
views=['lateral', 'medial', 'superior', 'inferior'],
show_legend=False, fill_title="THOMAS thalamic nuclei")
# B. Brainstem Navigator atlas
Brainstem_cmap = mcolors.LinearSegmentedColormap.from_list("", ["white", "#c8499b"])
plot_subcortical_data(atlas="Brainstem_Navigator", cmap=Brainstem_cmap, hemisphere='both',
views=['lateral', 'medial', 'superior', 'inferior'],
show_legend=False, fill_title="Brainstem Navigator")
3. Controlling transparency¶
Global alpha with fill_alpha¶
The fill_alpha argument controls the overall opacity of all regions (0 = fully transparent, 1 = fully opaque). This is particularly useful for atlases with many overlapping nuclei, where reducing transparency helps resolve individual regions. Here we loop through five alpha values for the Melbourne Subcortex S2 atlas:
import numpy as np
from subcortex_visualization.plotting import plot_subcortical_data
for fill_alpha in np.arange(0.2, 1.1, 0.2):
this_alpha_plot = plot_subcortical_data(atlas='Melbourne_S2', fill_alpha=fill_alpha,
cmap='plasma', show_legend=False)
Significance-based transparency¶
plot_subcortical_data also supports significance-based transparency through fill_by_significance=True. When enabled:
- Regions with
p_value < 0.05are rendered atfill_alpha(default 1.0) with full-weight borders - Regions with
p_value >= 0.05are rendered atnonsig_fill_alpha(default 0.5) with borders at0.25 × line_thickness
This requires a p_value column in your subcortex_data DataFrame. Here we simulate data for the SUIT cerebellar lobule atlas and flag the four regions with the largest absolute effect size as significant:
from subcortex_visualization.plotting import plot_subcortical_data
from subcortex_visualization.utils import get_atlas_regions
np.random.seed(128)
this_atlas = 'SUIT_cerebellar_lobule'
this_atlas_hemisphere_regions, this_atlas_vermis_regions = get_atlas_regions(this_atlas)
this_atlas_simdata = pd.DataFrame({
'region': np.concatenate([this_atlas_hemisphere_regions,
this_atlas_hemisphere_regions,
this_atlas_vermis_regions]),
'Hemisphere': (['L'] * len(this_atlas_hemisphere_regions) +
['R'] * len(this_atlas_hemisphere_regions) +
['V'] * len(this_atlas_vermis_regions))
})
this_atlas_simdata['value'] = np.random.normal(loc=0, scale=1, size=len(this_atlas_simdata))
this_atlas_simdata['p_value'] = np.random.uniform(low=0.05, high=1.0, size=len(this_atlas_simdata))
# Set the four largest-magnitude values to be significant (p < 0.05)
largest_indices = np.argsort(np.abs(this_atlas_simdata['value']))[-4:]
this_atlas_simdata.loc[largest_indices, 'p_value'] = 0.01
this_atlas_simdata.head()
| region | Hemisphere | value | p_value | |
|---|---|---|---|---|
| 0 | IV | L | -0.399999 | 0.113297 |
| 1 | V | L | 0.619174 | 0.204917 |
| 2 | VI | L | 0.698343 | 0.177087 |
| 3 | Crus_I | L | -1.253281 | 0.673136 |
| 4 | Crus_II | L | 0.459127 | 0.452322 |
plot_subcortical_data(atlas=this_atlas, subcortex_data=this_atlas_simdata, hemisphere='both',
midpoint=0, cmap='PiYG', show_legend=True, line_thickness=3,
fill_title="Simulated statistical test results",
fill_alpha=1.0, fill_by_significance=True, nonsig_fill_alpha=0.5)
4. Working with your own data¶
The key requirement for visualizing empirical data with plot_subcortical_data is that your region names exactly match those defined in the atlas (including capitalization and spacing). Use get_atlas_regions to check the exact names for any atlas:
# Most atlases return a 1D array of region names
aseg_regions = get_atlas_regions('aseg_subcortex')
print('aseg_subcortex regions:', aseg_regions)
# >>> get_atlas_regions('Melbourne_S1')
# ['hippocampus', 'amygdala', 'thalamus_posterior', 'thalamus_anterior',
# 'pallidum', 'accumbens', 'putamen', 'caudate']
# SUIT_cerebellar_lobule and Brainstem_Navigator return a tuple
suit_hemisphere_regions, suit_vermis_regions = get_atlas_regions('SUIT_cerebellar_lobule')
print('SUIT hemisphere regions:', suit_hemisphere_regions)
print('SUIT vermis regions:', suit_vermis_regions)
brainstem_hemisphere_regions, brainstem_midline_regions = get_atlas_regions('Brainstem_Navigator')
print('Brainstem hemisphere regions:', brainstem_hemisphere_regions)
print('Brainstem midline regions:', brainstem_midline_regions)
aseg_subcortex regions: ['thalamus' 'caudate' 'putamen' 'pallidum' 'hippocampus' 'amygdala' 'accumbens'] SUIT hemisphere regions: ['IV' 'V' 'VI' 'Crus_I' 'Crus_II' 'VIIb' 'VIIIa' 'VIIIb' 'IX' 'X'] SUIT vermis regions: ['VI' 'Crus_II' 'VIIb' 'VIIIa' 'VIIIb' 'IX' 'X'] Brainstem hemisphere regions: ['CnF' 'IC' 'ION' 'LC' 'LDTg_CGPn' 'LPB' 'MiTg_PBG' 'MPB' 'PCRtA' 'PnO_PnC' 'PTg' 'RN1' 'RN2' 'SC' 'SN1' 'SN2' 'SOC' 'SubC' 'Ve' 'VSM' 'VTA_PBP' 'iMRtl' 'iMRtm' 'isRt' 'mRta' 'mRtd' 'mRtl' 'sMRtl' 'sMRtm'] Brainstem midline regions: ['CLi_RLi' 'DR' 'MnR' 'PAG' 'PMnR' 'RMg' 'ROb' 'RPa']
Region names for each atlas are also listed on the package website.
Plotting continuous data¶
The subcortex_data DataFrame needs three columns:
region: region names matching the atlas exactlyvalue: numeric values to color-mapHemisphere:'L','R', or'B'(bilateral/midline regions)
Let's simulate random continuous data for the aseg subcortex atlas to demonstrate this:
import numpy as np
import pandas as pd
from subcortex_visualization.utils import get_atlas_regions
# Set random seed for reproducibility
np.random.seed(127)
# Get region names for the aseg subcortex atlas
aseg_subcortex_regions = get_atlas_regions("aseg_subcortex")
# Sample random values from a normal distribution for each hemisphere
example_continuous_data_L = (pd.DataFrame({
"region": aseg_subcortex_regions,
"value": np.random.normal(0, 1, len(aseg_subcortex_regions))
}).assign(Hemisphere="L"))
example_continuous_data_R = (pd.DataFrame({
"region": aseg_subcortex_regions,
"value": np.random.normal(0, 1, len(aseg_subcortex_regions))
}).assign(Hemisphere="R"))
# Combine left and right hemisphere data for bilateral plotting
example_continuous_data = pd.concat([example_continuous_data_L, example_continuous_data_R], axis=0)
example_continuous_data_L
| region | value | Hemisphere | |
|---|---|---|---|
| 0 | thalamus | -0.571809 | L |
| 1 | caudate | 0.029624 | L |
| 2 | putamen | 0.562592 | L |
| 3 | pallidum | -0.647652 | L |
| 4 | hippocampus | -0.845436 | L |
| 5 | amygdala | 0.106565 | L |
| 6 | accumbens | 1.732392 | L |
from subcortex_visualization.plotting import plot_subcortical_data
plot_subcortical_data(subcortex_data=example_continuous_data,
atlas='aseg_subcortex', hemisphere='both', cmap='viridis',
fill_title="Normal distribution sample, aseg subcortex atlas")
Custom colormaps and the midpoint argument¶
You can pass any Matplotlib colormap (or create a custom one with LinearSegmentedColormap) to the cmap argument.
Since this simulated data spans negative and positive values, a diverging blue-white-red colormap is a natural choice.
The midpoint argument specifies the center value of the color scale.
Setting this to 0 maps the center color to 0, with symmetric bounds determined automatically from the data range (unless you also set vmin and vmax explicitly).
Without explicitly setting vmin/vmax, the color range will be defined symmetrically around midpoint to capture the full range of the data.
from subcortex_visualization.plotting import plot_subcortical_data
import matplotlib.colors as mcolors
# Create a blue-white-red colormap
white_blue_red_cmap = mcolors.LinearSegmentedColormap.from_list("BlueWhiteRed",
["blue", "white", "red"])
plot_subcortical_data(subcortex_data=example_continuous_data,
atlas='aseg_subcortex',
hemisphere='both',
fill_title="Normal distribution sample, aseg subcortex atlas",
cmap=white_blue_red_cmap, midpoint=0)
5. Extracting regional statistics from volumetric data¶
In many cases, you may have a three-dimensional volumetric image (from any imaging modality) and want to reduce it down to region-level summary statistics before plotting. The parcel_segstats function handles this: it takes a NIfTI image and an atlas name and computes a user-specified summary statistic for all voxels in each region.
For the parc_stat argument, you can pass any function that takes an array of values and returns a single scalar, such as np.mean, np.std, np.median, or np.max. You can also pass a list of functions to compute multiple statistics simultaneously, in which case the output DataFrame will have one row per region per statistic.
The output DataFrame is pre-formatted for direct use with plot_subcortical_data (no reformatting needed).
Atlas space compatibility¶
The atlases are provided in two MNI spaces: MNI152NLin6Asym (default) and MNI152NLin2009cAsym.
Your input volume must be in one of these two spaces to use our package to compute region-aggregated statistics in one or more of the included atlases.
If the affine or spatial dimensions don't match, parcel_segstats will raise an error by default.
You can specify an interpolation method ('nearest', 'linear', or 'cubic') to resample the atlas to your input volume, though we encourage you to take care with atlas alignment and, wherever possible, normalize your data to the correct MNI space rather than relying on interpolation.
Here we compute the median CB1 receptor density per region in the aseg subcortex atlas from a group-averaged PET map:
import numpy as np
import matplotlib
from subcortex_visualization import segmentation, plotting
# Define file paths
CB1_density_URL = "https://github.com/netneurolab/hansen_receptorvar/blob/main/data/PET_volumes/CB1_fmpepVt2_hc20_nummenmaa_mean.nii.gz?raw=true"
# Download the CB1 density map to a local file path
import requests
response = requests.get(CB1_density_URL)
with open("CB1_density_mean.nii.gz", "wb") as f:
f.write(response.content)
functional_map_path = "CB1_density_mean.nii.gz"
atlas_name = "aseg_subcortex"
func_name = "CB1_density"
# The PET maps are shared in MNI152NLin6Asym space, so we specify
# this space for the atlas to ensure proper alignment
atlas_space = 'MNI152NLin6Asym'
# Apply the aseg subcortex atlas to extract the median value per region,
# specifying interpolation='nearest' to preserve discrete labels in the atlas
this_func_atlas_data = segmentation.parcel_segstats(
input_vol=functional_map_path,
atlas_space=atlas_space,
atlas=atlas_name,
func_name=func_name,
parc_stat=np.median,
interpolation='nearest'
)
# Custom color map
custom_cmap = mcolors.LinearSegmentedColormap.from_list("this_atlas_cmap", ["white", "#00405c"])
plotting.plot_subcortical_data(
subcortex_data=this_func_atlas_data,
atlas=atlas_name, value_column='value',
fill_title='Median CB1 density - aseg subcortex atlas',
hemisphere='both', show_legend=True, show_figure=True, cmap=custom_cmap
)
# Clean up the downloaded file
os.remove("CB1_density_mean.nii.gz")
Resampling atlas 'aseg_subcortex' to match input data affine and dimensions using nearest interpolation...
Applying multiple atlases simultaneously¶
parcel_segstats accepts a list of atlas names, running the parcellation for each and concatenating results into one DataFrame with an Atlas column. This makes it straightforward to compare how regional patterns differ across parcellation schemes.
As long as your empirical volume is in one of the two supported MNI spaces, you can flexibly mix any combination of atlases and summary statistics in a single function call.
Here we compute the mean GABAA-$\alpha$ 1 receptor subunit density across all twelve atlases included in the package:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from subcortex_visualization import segmentation, plotting
# Define subcortical atlases
all_atlases = ['aseg_subcortex', 'Melbourne_S1', 'Melbourne_S2',
'Melbourne_S3', 'Melbourne_S4', 'AICHA_subcortex',
'Brainnetome_subcortex', 'CIT168_subcortex', 'Thalamus_HCP',
'Thalamus_THOMAS', 'Brainstem_Navigator', 'SUIT_cerebellar_lobule']
parc_stat = np.nanmean
atlas_space = 'MNI152NLin6Asym'
# Define paths
GABAa1_density_URL = "https://github.com/netneurolab/hansen_receptorvar/blob/main/data/PET_volumes/GABAa1_ro154513_hc23_chang_mean.nii.gz?raw=true"
# Download the GABA_A_a1 density map to a local file path
response = requests.get(GABAa1_density_URL)
func_map_path = "GABAa1_density_mean.nii.gz"
with open(func_map_path, "wb") as f:
f.write(response.content)
func_name = "GABA_A_a1_density"
# Extract mean values for all atlases from the GABA_A_a1 density map
# Use nearest neighbor interpolation to preserve discrete labels in the atlas
func_map_subcortical_parc_df = segmentation.parcel_segstats(
input_vol=func_map_path,
atlas=all_atlases,
atlas_space=atlas_space,
parc_stat=parc_stat,
ignore_background=True,
func_name=func_name,
interpolation='nearest'
)
this_vmin = func_map_subcortical_parc_df['value'].min()
this_vmax = func_map_subcortical_parc_df['value'].max()
# Plot each atlas with a consistent color scale
for atlas in all_atlases:
this_atlas_data = func_map_subcortical_parc_df.query("Atlas == @atlas")
this_fill_title = f"{func_name} - {atlas}"
this_atlas_plot = plotting.plot_subcortical_data(
subcortex_data=this_atlas_data,
atlas=atlas,
value_column='value',
fill_title=this_fill_title,
hemisphere='both',
vmin=this_vmin,
vmax=this_vmax,
cmap='plasma',
show_legend=True,
show_figure=False
)
Resampling atlas 'aseg_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S1' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S2' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S3' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S4' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'AICHA_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Brainnetome_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'CIT168_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Thalamus_HCP' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Thalamus_THOMAS' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CLi_RLi' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CnF_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CnF_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'DR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'IC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'IC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ION_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ION_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LDTg_CGPn_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LDTg_CGPn_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LPB_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LPB_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MPB_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MPB_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MiTg_PBG_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MiTg_PBG_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MnR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PAG' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PCRtA_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PCRtA_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PMnR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PTg_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PTg_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PnO_PnC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PnO_PnC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RMg' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN1_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN1_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN2_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN2_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ROb' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RPa' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN1_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN1_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN2_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN2_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SOC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SOC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SubC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SubC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VSM_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VSM_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VTA_PBP_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VTA_PBP_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'Ve_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'Ve_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtm_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtm_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'isRt_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'isRt_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRta_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRta_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtd_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtd_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtm_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtm_r' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'SUIT_cerebellar_lobule' to match input data affine and dimensions using nearest interpolation...
6. Combining cortical and subcortical visualizations¶
Since plot_subcortical_data returns a Matplotlib Figure object (when show_figure=False), Python users can save it alongside any other figure (e.g., a cortical surface plot from ggseg in R or a nilearn glass brain) and combine them in their preferred image editor, like Inkscape or Adobe Illustrator.
To compute cortical parcellation statistics in Python, we can use the neuromaps package to project a volumetric PET map to a surface and then parcellate it with any parcellation scheme.
The example below uses the Schaefer 1000-parcel atlas in fsaverage space.
Note: This section requires additional dependencies:
netneurotools,neuromaps, and Connectome Workbench. See the neuromaps documentation for installation instructions.
import numpy as np
import pandas as pd
from subcortex_visualization import segmentation, plotting
# For cortical data visualization
from netneurotools import datasets as nntdata
import neuromaps
from neuromaps import transforms, images
from neuromaps.parcellate import Parcellater
from neuromaps.images import dlabel_to_gifti
# Add connectome workbench to path; adjust for your system
os.environ['PATH'] = os.environ['PATH'] + ':/Applications/workbench/bin_macosx64'
# Define file paths
func_map_path = "GABAa1_density_mean.nii.gz"
func_name = "GABA_A_a1_density"
# Transform functional map to fsaverage surface
func_map_in_surf = transforms.mni152_to_fsaverage(func_map_path, method='nearest')
# Download and read in the Schaefer 1000-parcel lookup table
s1000_url = (
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/master/stable_projects/"
"brain_parcellation/Schaefer2018_LocalGlobal/Parcellations/MNI/freeview_lut/"
"Schaefer2018_1000Parcels_17Networks_order.txt"
)
schaefer1000_lookup = pd.read_csv(s1000_url, header=None, sep='\t')[[0, 1]]
schaefer1000_lookup.columns = ['index', 'label']
schaefer1000_labels = schaefer1000_lookup.label.tolist()
# Download Schaefer parcellations in fsaverage space
schaefer_fsaverage = nntdata.fetch_schaefer2018(version='fsaverage')
schaefer_fsaverage_1000 = schaefer_fsaverage['1000Parcels17Networks']
# Convert 1000-parcellation into gifti space
schaefer_res_gifti = neuromaps.images.annot_to_gifti(schaefer_fsaverage['1000Parcels17Networks'])
schaefer_res_gifti = neuromaps.images.relabel_gifti(schaefer_res_gifti)
# Fit a Parcellator object
parc = Parcellater(schaefer_res_gifti, 'fsaverage', resampling_target='parcellation')
func_map_in_surf_parc = parc.fit_transform(func_map_in_surf, 'fsaverage',
ignore_background_data=True)
# Create a dataframe with the parcellated cortical data
func_map_cortical_parc_df = (pd.DataFrame({
'region': schaefer1000_labels,
'Atlas': 'Schaefer1000_17Networks',
'value': func_map_in_surf_parc
}).assign(Hemisphere=lambda x: np.where(x['region'].str.contains('LH'), 'L', 'R')))
Please cite the following papers if you are using this function:
[primary]:
Alexander Schaefer, Ru Kong, Evan M Gordon, Timothy O Laumann, Xi-Nian Zuo, Avram J Holmes, Simon B Eickhoff, and BT Thomas Yeo. Local-global parcellation of the human cerebral cortex from intrinsic functional connectivity mri. Cerebral cortex, 28(9):3095–3114, 2018.
Dataset atl-schaefer2018 already exists. Skipping download.
With the cortical data in hand, we can now combine it with subcortical parcellation results from parcel_segstats and maintain a consistent color scale across all brain systems. Note that the color range is consistent across all plots, allowing for direct comparison of receptor density across cortical and non-cortical structures.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from subcortex_visualization import segmentation, plotting
# Define subcortical atlases
all_atlases = ['aseg_subcortex', 'Melbourne_S1', 'Melbourne_S2',
'Melbourne_S3', 'Melbourne_S4', 'AICHA_subcortex',
'Brainnetome_subcortex', 'CIT168_subcortex', 'Thalamus_HCP',
'Thalamus_THOMAS', 'Brainstem_Navigator', 'SUIT_cerebellar_lobule']
atlas_space = 'MNI152NLin6Asym'
func_map_path = "GABAa1_density_mean.nii.gz"
func_name = "GABA_A_a1_density"
# Extract mean values for all atlases from the GABA_A_a1 density map
# Use nearest neighbor interpolation to preserve discrete labels in the atlas
func_map_subcortical_parc_df = segmentation.parcel_segstats(
input_vol=func_map_path,
atlas=all_atlases,
atlas_space=atlas_space,
parc_stat=np.nanmean,
ignore_background=True,
func_name=func_name,
interpolation='nearest'
)
# Combine with cortical data from previous code cell
func_map_all_parc_df = pd.concat([func_map_cortical_parc_df, func_map_subcortical_parc_df], axis=0)
# Get min and max values across all atlases for consistent color scaling
this_vmin = func_map_all_parc_df['value'].min()
this_vmax = func_map_all_parc_df['value'].max()
# Plot each atlas with consistent color scale
for atlas in all_atlases:
this_atlas_data = func_map_all_parc_df.query("Atlas == @atlas")
this_fill_title = f"{func_name} - {atlas}"
this_atlas_plot = plotting.plot_subcortical_data(
subcortex_data=this_atlas_data,
atlas=atlas,
value_column='value',
fill_title=this_fill_title,
hemisphere='both',
vmin=this_vmin,
vmax=this_vmax,
cmap='plasma',
show_legend=False,
show_figure=False
)
# Clean up the downloaded file
os.remove("GABAa1_density_mean.nii.gz")
Resampling atlas 'aseg_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S1' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S2' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S3' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Melbourne_S4' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'AICHA_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Brainnetome_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'CIT168_subcortex' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Thalamus_HCP' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'Thalamus_THOMAS' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CLi_RLi' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CnF_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'CnF_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'DR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'IC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'IC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ION_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ION_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LDTg_CGPn_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LDTg_CGPn_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LPB_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'LPB_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MPB_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MPB_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MiTg_PBG_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MiTg_PBG_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'MnR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PAG' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PCRtA_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PCRtA_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PMnR' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PTg_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PTg_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PnO_PnC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'PnO_PnC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RMg' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN1_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN1_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN2_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RN2_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'ROb' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'RPa' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN1_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN1_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN2_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SN2_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SOC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SOC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SubC_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'SubC_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VSM_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VSM_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VTA_PBP_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'VTA_PBP_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'Ve_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'Ve_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtm_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'iMRtm_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'isRt_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'isRt_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRta_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRta_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtd_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtd_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'mRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtl_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtl_r' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtm_l' to match input data affine and dimensions using nearest interpolation... Resampling ROI 'sMRtm_r' to match input data affine and dimensions using nearest interpolation... Resampling atlas 'SUIT_cerebellar_lobule' to match input data affine and dimensions using nearest interpolation...