Visualizing colocalization results
NiSpace has two integrated visualization methods that work directly on the fitted NiSpace object:
nsp.plot()— colocalization results (bar charts, scatter plots, …)nsp.plot_brain()— brain surface or volume rendering of input/reference maps
This notebook walks through the customization options for both. For standalone brain plotting (independent of any NiSpace analysis), see Notebook 8.
[1]:
import tqdm.notebook
tqdm.notebook.tqdm = tqdm.tqdm
import numpy as np
import matplotlib.pyplot as plt
[2]:
from nispace.datasets import fetch_reference, fetch_example
from nispace.io import load_img
from nispace.api import NiSpace
# set up two analyses: pain map (single map) and anorexia nervosa (group comparison)
pet_maps = fetch_reference("pet", parcellation="Schaefer200",
collection="UniqueTracers", print_references=False)
# single map: pain
nsp_pain = NiSpace(x=pet_maps, y=load_img("neuroquery/pain.nii.gz"),
y_labels="Pain", parcellation="Schaefer200", seed=42, n_proc=4)
nsp_pain.fit()
nsp_pain.colocalize("spearman")
nsp_pain.permute("maps", n_perm=1000, p_tails="upper")
# group comparison: anorexia nervosa (simulated data — not for scientific use)
an_data = fetch_example("anorexianervosa", parcellation="Schaefer200")
groups = an_data.index.str.extract(r'(AN|HC)$')[0].values
nsp_an = NiSpace(x=pet_maps, y=an_data, parcellation="Schaefer200", seed=42, n_proc=4)
nsp_an.fit()
nsp_an.transform_y("hedges(a,b)", groups=groups)
nsp_an.colocalize("spearman")
nsp_an.permute("groups", groups=groups, n_perm=1000)
INFO | 01/06/26 15:04:11 | nispace.datasets: Loading pet maps.
INFO | 01/06/26 15:04:11 | nispace.datasets: Loading integrated collection 'UniqueTracers' for dataset 'pet'.
INFO | 01/06/26 15:04:11 | nispace.datasets: Filtering maps by collection.
INFO | 01/06/26 15:04:11 | nispace.datasets: Loading data parcellated with 'Schaefer200Parcels7Networks'
INFO | 01/06/26 15:04:11 | nispace.api: *** NiSpace.fit() - Data extraction and preparation. ***
INFO | 01/06/26 15:04:11 | nispace.core.parcellation: Building multi-space Parcellation for 'Schaefer200Parcels7Networks' from library.
INFO | 01/06/26 15:04:11 | nispace.core.parcellation: Available spaces: MNI152NLin2009cAsym, MNI152NLin6Asym, fsaverage, fsLR
INFO | 01/06/26 15:04:11 | nispace.core.parcellation: Parcellation 'Schaefer200Parcels7Networks': validation passed.
INFO | 01/06/26 15:04:11 | nispace.core.parcellation: Lazy-loading parcellation image for space 'MNI152NLin2009cAsym'.
INFO | 01/06/26 15:04:11 | nispace.core.parcellation: Parcellation 'Schaefer200Parcels7Networks': active space set to 'MNI152NLin2009cAsym'.
INFO | 01/06/26 15:04:11 | nispace.api: Checking input data for 'x' (should be, e.g., PET data):
INFO | 01/06/26 15:04:11 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
WARNING | 01/06/26 15:04:11 | nispace.io: Parcellated data contains nan values!
INFO | 01/06/26 15:04:11 | nispace.api: Got 'x' data for 29 x 200 parcels.
INFO | 01/06/26 15:04:11 | nispace.api: Checking input data for 'y' (should be, e.g., subject data):
INFO | 01/06/26 15:04:11 | nispace.io: Input type: list, assuming imaging data.
INFO | 01/06/26 15:04:11 | nispace.io: Background (bg) handling: ignoring bg: True (bg value: ['auto', 0.0]); dropping bg parcels: False
INFO | 01/06/26 15:04:11 | nispace.io: Parcellating imaging data.
Parcellating (4 proc): 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 107.95it/s]
INFO | 01/06/26 15:04:15 | nispace.api: Got 'y' data for 1 x 200 parcels.
INFO | 01/06/26 15:04:15 | nispace.api: Z-standardizing 'X' data.
INFO | 01/06/26 15:04:15 | nispace.api: *** NiSpace.colocalize() - Estimating X & Y colocalizations. ***
INFO | 01/06/26 15:04:15 | nispace.api: Running 'spearman' colocalization.
INFO | 01/06/26 15:04:15 | nispace.api: Pre-ranking X and Y data.
Colocalizing (spearman, 4 proc): 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1375.18it/s]
INFO | 01/06/26 15:04:18 | nispace.api: *** NiSpace.permute() - Estimate exact non-parametric p values. ***
INFO | 01/06/26 15:04:18 | nispace.api: Permutation of: X maps.
INFO | 01/06/26 15:04:18 | nispace.api: Using default null method 'alexander_bloch' (parcellation null space: 'fsLR').
INFO | 01/06/26 15:04:18 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsLR'.
INFO | 01/06/26 15:04:18 | nispace.api: Loading observed colocalizations (method = 'spearman').
INFO | 01/06/26 15:04:18 | nispace.core.permute: No null maps found.
INFO | 01/06/26 15:04:18 | nispace.core.permute: Generating null maps (n = 1000, null_method = 'alexander_bloch').
INFO | 01/06/26 15:04:18 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsaverage'.
INFO | 01/06/26 15:04:18 | nispace.nulls: Null map generation: Assuming n = 29 data vector(s) for n = 200 parcels.
INFO | 01/06/26 15:04:18 | nispace.nulls: Using provided precomputed spin matrix.
Spin null maps: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 29/29 [00:00<00:00, 239.60it/s]
INFO | 01/06/26 15:04:18 | nispace.nulls: Null data generation finished.
INFO | 01/06/26 15:04:18 | nispace.core.permute: Z-standardizing null maps.
Processing null arrays (4 proc): 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:01<00:00, 981.23it/s]
Null colocalizations (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 7625.62it/s]
INFO | 01/06/26 15:04:21 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'upper'}).
INFO | 01/06/26 15:04:21 | nispace.datasets: Loading example dataset: 'anorexianervosa', parcellated with: Schaefer200Parcels7Networks.
INFO | 01/06/26 15:04:22 | nispace.core.parcellation: Building multi-space Parcellation for 'Schaefer200Parcels7Networks' from library.
INFO | 01/06/26 15:04:22 | nispace.core.parcellation: Available spaces: MNI152NLin2009cAsym, MNI152NLin6Asym, fsaverage, fsLR
INFO | 01/06/26 15:04:22 | nispace.core.parcellation: Parcellation 'Schaefer200Parcels7Networks': validation passed.
INFO | 01/06/26 15:04:22 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
WARNING | 01/06/26 15:04:22 | nispace.io: Parcellated data contains nan values!
INFO | 01/06/26 15:04:22 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
Colocalizing (spearman, 4 proc): 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1825.20it/s]
INFO | 01/06/26 15:04:22 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsLR'.
Permuting groups (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 918192.64it/s]
Null transformations (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 4355.74it/s]
Processing null arrays (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 4054.51it/s]
Null colocalizations (spearman, 4 proc): 100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 14453.60it/s]
INFO | 01/06/26 15:04:22 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'two'}).
[2]:
<nispace.api.NiSpace at 0x10b441b50>
nsp.plot() — colocalization results
Default output
The default plot() shows a categorical bar chart of colocalization values, colored by neurotransmitter system, with the null distribution shaded in grey.
[3]:
nsp_pain.plot()
INFO | 01/06/26 15:04:23 | nispace.plotting: Significance annotation: 2/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
[3]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x31769adc0>)
Values: raw correlations, z-scores, or -log10(p)
The values argument controls what’s shown on the x-axis:
"coloc"— raw colocalization values (Spearman ρ, partial r, …)"z"— null-normalized z-scores (colocalization scaled by null mean and sd)"p"— −log₁₀(p), optionally with multiple comparison correction
[4]:
# z-normalized values (makes effect sizes more comparable across maps)
nsp_pain.normalize_colocalizations() # we need to calculate the normalized scores first
nsp_pain.plot(values="z")
INFO | 01/06/26 15:04:23 | nispace.plotting: Significance annotation: 2/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
[4]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$ | normalized)"}, xlabel='$Rho$ (null-normalized Z)'>,
<seaborn._core.plot.Plotter at 0x318a3e220>)
[5]:
# -log10(p), with FDR correction marked
nsp_pain.plot(values="p")
[5]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$ | $p_{\\mathrm{uncorrected}}$)"}, xlabel='$-\\log_{10}(p)$'>,
<seaborn._core.plot.Plotter at 0x318a2cdf0>)
Sorting and filtering
Use sort_by to reorder the bars, and X_maps to select a subset of reference maps.
[6]:
# sort by colocalization value
nsp_pain.plot(sort_by="coloc")
INFO | 01/06/26 15:04:24 | nispace.plotting: Significance annotation: 2/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
[6]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x318ca5610>)
[7]:
# show only specific systems
nsp_pain.plot(
X_maps=["VAChT", "NET", "MOR", "KOR", "CB1"], # match by partial name
sort_by="coloc"
)
INFO | 01/06/26 15:04:24 | nispace.plotting: Significance annotation: 2/5 p_uncorrected < 0.05, 0/5 p_corrected < 0.05 (no correction applied)
[7]:
(<Figure size 500x250 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x318daccd0>)
P-value annotations
annot_p controls how significance markers are shown
"stats"— show filled and empty significance stars (default; filled -> corrected; empty -> raw values)"text"- show raw p values as text"both"— show stars and raw p value as textFalse— no markers
[8]:
nsp_pain.plot(annot_p="text")
INFO | 01/06/26 15:04:24 | nispace.plotting: Significance annotation: 2/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
[8]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x318f2dc40>)
Passing to matplotlib
plot() returns (fig, ax, plot_object), so you can post-process the figure with standard matplotlib calls. You can also pass fig and ax to embed the plot in an existing figure.
[9]:
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# important: also pass the figure level, otherwise legends will behave quite unpredictably
nsp_pain.plot(ax=axes[0], fig=fig, show=False, title="Pain map", sort_by="coloc")
nsp_an.plot(ax=axes[1], fig=fig, show=False, title="Anorexia nervosa (Hedges' g)", sort_by="coloc")
plt.tight_layout()
plt.show()
INFO | 01/06/26 15:04:25 | nispace.plotting: Significance annotation: 2/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
INFO | 01/06/26 15:04:25 | nispace.plotting: Significance annotation: 9/29 p_uncorrected < 0.05, 0/29 p_corrected < 0.05 (no correction applied)
nsp.plot_brain() — brain surface rendering
Basic usage
[10]:
# plot the Y data (pain map after parcellation)
nsp_pain.plot_brain(data="Y")
WARNING | 01/06/26 15:04:25 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:25 | nispace.plotting: brainplot: threshold='auto' → 0.005351351574063301
INFO | 01/06/26 15:04:25 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[10]:
(<Figure size 720x180 with 6 Axes>, [<Axes: >])
[11]:
# plot a specific reference map
nsp_pain.plot_brain(data="X", maps="VAChT", symmetric_cmap=False)
WARNING | 01/06/26 15:04:28 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:28 | nispace.plotting: brainplot: threshold='auto' → 0.019260043278336525
INFO | 01/06/26 15:04:28 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[11]:
(<Figure size 720x180 with 6 Axes>, [<Axes: >])
Rendering modes
The kind argument controls the rendering:
"surface"— inflated cortical surface (default for cortex-only parcellations)"glass"— glass brain (transparent volumetric rendering)"slice"— anatomical slices
[12]:
nsp_pain.plot_brain(data="Y", kind="glass")
WARNING | 01/06/26 15:04:32 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:32 | nispace.plotting: brainplot: threshold='auto' → 0.005351351574063301
INFO | 01/06/26 15:04:32 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[12]:
(<Figure size 720x180 with 6 Axes>, [<Axes: >])
[13]:
nsp_pain.plot_brain(data="Y", kind="slice")
WARNING | 01/06/26 15:04:35 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:35 | nispace.plotting: brainplot: threshold='auto' → 0.005351351574063301
INFO | 01/06/26 15:04:35 | nispace.plotting: brainplot: kind='slice', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[13]:
(<Figure size 700x180 with 7 Axes>, [<Axes: >])
Colormap and scale options
[14]:
# custom colormap, fixed scale, colorbar label
nsp_pain.plot_brain(
data="Y",
cmap="viridis",
symmetric_cmap=False,
vmin=0,
colorbar_label="z-score"
)
WARNING | 01/06/26 15:04:39 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:39 | nispace.plotting: brainplot: threshold='auto' → 0.005351351574063301
INFO | 01/06/26 15:04:39 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[14]:
(<Figure size 720x180 with 6 Axes>, [<Axes: >])
[15]:
# plot multiple X maps with a shared color scale
nsp_pain.plot_brain(
data="X",
maps=["VAChT", "NET", "KOR"], # only these three
shared_colorscale=True,
ncols=3,
symmetric_cmap=False
)
WARNING | 01/06/26 15:04:43 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:43 | nispace.plotting: brainplot: threshold='auto' → 0.00224649952724576
INFO | 01/06/26 15:04:43 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='inflated'
[15]:
(<Figure size 2160x180 with 18 Axes>, [<Axes: >, <Axes: >, <Axes: >])
Surface mesh options
[16]:
# pial surface instead of inflated
nsp_pain.plot_brain(data="Y", surf_mesh="pial")
WARNING | 01/06/26 15:04:54 | nispace.plotting: Brain plotting in NiSpace is experimental. If things look off, feel free to raise a GitHub issue!
INFO | 01/06/26 15:04:54 | nispace.plotting: brainplot: threshold='auto' → 0.005351351574063301
INFO | 01/06/26 15:04:54 | nispace.plotting: brainplot: kind='glass', img_mode='None', surf_space='None', mni_space='MNI152NLin2009cAsym', surf_mesh='pial'
[16]:
(<Figure size 720x180 with 6 Axes>, [<Axes: >])
Summary
``nsp.plot()`` key arguments:
Argument |
Options |
Effect |
|---|---|---|
|
|
What to show on x-axis |
|
|
Multiple comparison correction |
|
|
Bar ordering |
|
|
Significance markers |
|
list of strings |
Select subset of reference maps |
|
matplotlib Axes |
Embed in existing figure |
``nsp.plot_brain()`` key arguments:
Argument |
Options |
Effect |
|---|---|---|
|
|
Which maps to render |
|
string or list |
Select subset of maps |
|
|
Rendering mode |
|
any matplotlib colormap |
Colormap |
|
bool |
Zero-centered color scale |
|
bool |
Same scale across all maps |
Next: Notebook 8 covers the standalone brainplot() function — useful whenever you want to visualize brain maps outside of a NiSpace analysis.