netneurotools.plotting.pv_plot_subcortex

netneurotools.plotting.pv_plot_subcortex(parcel_data, template, include_keys=None, custom_surfaces=None, hemi='both', layout='default', cmap='viridis', clim=None, panel_size=(500, 400), zoom_ratio=1.4, show_colorbar=True, show_silhouette=False, parallel_projection=True, cbar_title=None, show_plot=True, jupyter_backend='static', lighting_style='default', save_fig=None, plotter_kws=None, mesh_kws=None, cbar_kws=None, silhouette_kws=None, force_fetch=False, data_dir=None, verbose=0)[source]

Plot subcortical data using PyVista.

This function provides a flexible interface for visualizing parcellated subcortical data on standard neuroimaging atlases. It supports custom surfaces, multiple layouts, and various customization options for publication-quality visualizations.

Parameters:
  • parcel_data (dict) – Dictionary mapping region identifiers to data values. Keys should match the region identifiers in the selected template atlas (e.g., ‘10’ for a specific region in aseg template).

  • template (str) – Atlas template to use for subcortical visualization. A pre-computed subcortical surface or a custom surface can be used. Options:

    • ‘aseg’: FreeSurfer automatic segmentation

    • ‘tianS1’, ‘tianS2’, ‘tianS3’, ‘tianS4’: Tian et al. subcortical atlas

    • ‘custom’: User-provided custom surfaces (requires custom_surfaces)

  • include_keys (list or tuple of lists, optional) – Region identifiers to include in the visualization. If hemi is “both”, this should be a tuple of two lists (left, right). If None, will use all keys from parcel_data. Default is None.

  • custom_surfaces (dict, optional) – Dictionary mapping region identifiers (as strings) to PyVista mesh objects. Only used when template=’custom’. Should be generated with _pv_make_subcortex_surfaces() or similar. Default is None.

  • hemi (str, optional) – Hemisphere to plot. Options: ‘L’ (left), ‘R’ (right), ‘both’. Default is ‘both’.

  • layout (str, optional) – Layout of the plot panels:

    • ‘default’: 2x2 grid for both hemispheres, 1x2 for single hemisphere

    • ‘single’: Single panel (useful for custom views)

    • ‘row’: Horizontal arrangement of all views

    • ‘column’: Vertical arrangement of all views

    Default is ‘default’.

  • cmap (str, optional) – Matplotlib colormap name. Default is ‘viridis’.

  • clim (tuple of float, optional) – Colorbar limits as (vmin, vmax). If None, will be set to 2.5th and 97.5th percentiles of the data. Default is None.

  • panel_size (tuple of int, optional) – Size of each panel in pixels as (width, height). Default is (500, 400).

  • zoom_ratio (float, optional) – Camera zoom level. Values > 1.0 zoom in, < 1.0 zoom out. Default is 1.4.

  • show_colorbar (bool, optional) – Whether to display the colorbar. Default is True.

  • show_silhouette (bool, optional) – Whether to add silhouette edges to the meshes for enhanced visibility. Default is False.

  • parallel_projection (bool, optional) – Whether to use parallel projection for the camera. Default is True.

  • cbar_title (str, optional) – Title text for the colorbar. Default is None.

  • show_plot (bool, optional) – Whether to display the plot immediately. Set to False to return the plotter object for further customization. Default is True.

  • jupyter_backend (str, optional) – Backend for Jupyter notebook rendering. See PyVista documentation for available options (‘html’, ‘static’, ‘trame’, etc.). Set to None for non-notebook environments. Default is ‘static’.

  • lighting_style (str, optional) – Lighting style preset:

    • ‘default’, ‘lightkit’: Standard three-point lighting

    • ‘threelights’: Alternative three-light setup

    • ‘metallic’, ‘plastic’, ‘shiny’, ‘glossy’: Material presets

    • ‘ambient’, ‘plain’: Flat lighting styles

    Default is ‘default’.

  • save_fig (str or Path, optional) – Path to save the figure. Supported formats: .png, .jpeg, .jpg, .bmp, .tif, .tiff (raster); .svg, .eps, .ps, .pdf, .tex (vector). Default is None (no save).

Returns:

pl – PyVista plotter object. Can be further customized before calling pl.show() if show_plot=False.

Return type:

pyvista.Plotter

Other Parameters:
  • plotter_kws (dict, optional) – Additional keyword arguments to pass to pyvista.Plotter. Default is None.

  • mesh_kws (dict, optional) – Additional keyword arguments to pass to pyvista.Plotter.add_mesh(). Default is None.

  • cbar_kws (dict, optional) – Additional keyword arguments to pass to pyvista.Plotter.add_scalar_bar(). Default is None.

  • silhouette_kws (dict, optional) – Additional keyword arguments to pass to pyvista.Plotter.add_silhouette(). Only used when show_silhouette=True. Default is None.

  • force_fetch (bool, optional) – If True, will re-download template data even if cached locally. Recommended to use periodically to refresh data. Default is False.

  • data_dir (str or Path, optional) – Path to use as data directory. If not specified, will check for environmental variable ‘NNT_DATA’; if that is not set, will use ~/nnt-data instead. Default: None

  • verbose (int, optional) – Modifies verbosity of download, where higher numbers mean more updates. Default: 0

Notes

Available templates:

  • ‘aseg’: FreeSurfer’s automatic brain segmentation, includes major subcortical structures (thalamus, striatum, hippocampus, etc.) Generated from tpl-MNI152NLin2009cAsym_res-01_seg-aseg_dseg.nii.gz from TemplateFlow. See FreeSurferColorLUT for region IDs.

  • ‘tianS1-S4’: Multi-level atlases from Tian et al. [1] providing finer subdivisions of subcortical structures. Generated from Group-Parcellation/3T/Subcortex-Only/Tian_Subcortex_S{1,2,3,4}_3T_2009cAsym.nii.gz.

  • ‘custom’: User-provided atlas generated from volumetric data using _pv_make_subcortex_surfaces()

Template data updates:

Subcortical surface templates are periodically updated to add new atlases and optimize existing surface quality. To ensure you have the latest versions, it’s recommended to set force_fetch=True occasionally to re-download and refresh your local cached data. This is especially important when:

  • New atlas templates are announced

  • Surface quality improvements are released

  • You encounter rendering issues with cached surfaces

  • Starting a new publication project

Data format:

The parcel_data dictionary maps region identifiers to scalar values:

>>> parcel_data = {
...     '10': 0.5,  # thalamus
...     '11': 0.7,  # caudate
...     '12': 0.6,  # putamen
... }

Region identifiers depend on the selected template (integer IDs from FreeSurfer, Tian atlas, etc.).

There is no intrinsic left/right distinction in subcortical atlases, so include_keys must specify which regions to plot for each hemisphere.

For example, this is usually how you would set include_keys, when you want left and right structures plotted in their respective hemisphere panels. >>> include_keys = ([‘10’, ‘11’, ‘12’], [‘49’, ‘50’, ‘51’]) # doctest: +SKIP

In contrast, the following will display BOTH left and right structures in BOTH hemisphere panels: >>> include_keys = ([‘10’, ‘11’, ‘12’, ‘49’, ‘50’, ‘51’]) # doctest: +SKIP

Layouts:

  • ‘default’: Shows medial and lateral views for each hemisphere

  • ‘single’: Shows only one view (useful for custom camera angles)

  • ‘row’/’column’: Linear arrangements of all standard views

Parallel projection:

When comparing subcortical structures of very different sizes (e.g., thalamus vs. amygdala), parallel_projection=True provides scale-invariant visualization, while parallel_projection=False uses perspective projection.

Lighting styles:

Different lighting presets affect surface appearance through ambient, diffuse, specular, and specular_power parameters:

  • Metallic: Low ambient (0.1), high specular (1.0)

  • Plastic: Balanced properties, moderate specular (0.3)

  • Shiny: High specular (0.8), high specular_power (50)

Jupyter notebooks:

When using in Jupyter, the function automatically sets notebook=True and off_screen=True for proper rendering.

There can be various issues when plotting in Jupyter notebooks depending on your environment. For troubleshooting and detailed configuration options, see:

Backend selection:

Choose the appropriate jupyter_backend for your use case:

  • ‘trame’: Best performance and interactivity (recommended)

  • ‘html’: Good interactivity, works in most environments

  • ‘static’: No interactivity but reliable fallback option

If trame does not work in your environment, try html. The static option should always work as a last resort.

Customization with keyword arguments:

The plotter_kws, mesh_kws, cbar_kws, and silhouette_kws parameters allow flexible overriding of default settings. For example:

  • plotter_kws={‘window_size’: (2000, 1500)} for higher resolution

  • mesh_kws={‘ambient’: 0.5} to adjust material properties

  • cbar_kws={‘n_labels’: 5} for more colorbar labels

  • silhouette_kws={‘feature_angle’: 30} to adjust edge detection sensitivity

References

Examples

Basic usage:

Plot random data on aseg template:

>>> from netneurotools.plotting import pv_plot_subcortex
>>> parcel_data = {
...     '10': 0.5, '11': 0.7, '12': 0.6, '13': 0.8,
...     '49': 0.4, '50': 0.6, '51': 0.5, '52': 0.7
... }
>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg"
...     include_keys=(['10', '11', '12', '13'], ['49', '50', '51', '52'])
... )

Different atlases:

Use Tian atlas with finer subcortical subdivisions:

>>> parcel_data_tian = {str(k+1): v for k, v in enumerate(
...     np.random.random(16)
... )}
>>> pl = pv_plot_subcortex(
...     parcel_data_tian,
...     template="tianS2"  # Tian level 2 atlas
... )

Single hemisphere:

Plot only left hemisphere with custom include_keys:

>>> include_keys = ['10', '11', '12']  # thalamus, caudate, putamen
>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     hemi="L",
...     include_keys=include_keys,
... )

Different layouts:

Compare all layout options:

>>> for layout in ["default", "single", "row", "column"]:
...     pl = pv_plot_subcortex(
...         parcel_data,
...         template="aseg",
...         layout=layout,
...         cbar_title=f"Layout: {layout}"
...     )

Custom colorbar and limits:

Set explicit color limits and colorbar title:

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     cmap="RdBu_r",
...     clim=(-1, 1),  # Symmetric limits
...     cbar_title="Activation (z-score)",
... )

Silhouette edges:

Add edge outlines for clarity, for example, this simulates the 2D flat drawing style:

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     lighting_style="none",
...     show_silhouette=True,
...     silhouette_kws={'color': 'black', 'line_width': 5},
... )

Saving figures:

Save as high-resolution PNG:

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     save_fig="subcortex_plot.png",
... )

Save as vector graphics (SVG):

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     save_fig="subcortex_plot.svg",
... )

Lighting styles:

Explore different lighting presets:

>>> for style in ["metallic", "plastic", "shiny", "glossy"]:
...     pl = pv_plot_subcortex(
...         parcel_data,
...         template="aseg",
...         lighting_style=style,
...         cbar_title=f"Style: {style}",
...         save_fig=f"subcortex_{style}.png",
...     )

Advanced customization:

Combine multiple options for publication-quality figures:

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="tianS2",
...     hemi="both",
...     layout="row",
...     cmap="plasma",
...     clim=(0, 1),
...     zoom_ratio=1.6,
...     show_colorbar=True,
...     cbar_title="Regional Connectivity",
...     lighting_style="shiny",
...     parallel_projection=True,
...     save_fig="publication_figure.png",
...     jupyter_backend=None,  # For script usage
... )

Custom surfaces:

Use user-defined subcortical surfaces generated from volumetric data:

>>> from netneurotools.plotting import _pv_make_subcortex_surfaces
>>> # Assuming atlas.nii.gz contains custom volumetric segmentation
>>> custom_surfs = _pv_make_subcortex_surfaces(
...     "atlas.nii.gz",
...     include_keys=[1, 2, 3]
... )
>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="custom",
...     custom_surfaces=custom_surfs,
... )

Plotter customization for further manipulation:

Return plotter object without showing to add custom elements:

>>> pl = pv_plot_subcortex(
...     parcel_data,
...     template="aseg",
...     show_plot=False,  # Don't show yet
... )
>>> # Add custom annotations
>>> pl.add_text("Custom Title", position="upper_edge")
>>> pl.show()