Advanced topics
This notebook covers advanced features of NiSpace:
reduce_x()) — summarize a large set of reference maps into a smaller number of components before colocalization.We use the ENIGMA reference datasets (enigmathick and enigmaarea) as our input maps here — these contain published cortical thickness and surface area effect sizes (Cohen’s d) for many psychiatric disorders vs. healthy controls. Note that we’re using reference data as our input maps, which, in the case of ENIGMA maps, is however often done: it lets us ask questions like “which reference systems explain patterns of grey matter alterations across psychiatric disorders?”
[1]:
import tqdm.notebook
tqdm.notebook.tqdm = tqdm.tqdm
import numpy as np
import pandas as pd
[2]:
from nispace.datasets import fetch_reference, fetch_example
from nispace.api import NiSpace
# ENIGMA cortical thickness effect sizes (Cohen's d, cases vs. controls)
# These are reference datasets — we use them as target maps (Y) here
enigma_thick = fetch_reference("enigmathick", parcellation="DesikanKilliany", collection="Adult",
print_references=False)
print(f"ENIGMA thickness: {enigma_thick.shape[0]} disorders x {enigma_thick.shape[1]} cortical parcels")
print("Disorders:", list(enigma_thick.index))
# Continuous resting-state network (RSN) reference maps for colocalization
pet_maps = fetch_reference("rsn", parcellation="DesikanKilliany",
print_references=False)
print(f"PET: {pet_maps.shape[0]} maps x {pet_maps.shape[1]} parcels")
INFO | 01/06/26 15:07:43 | nispace.datasets: Loading enigmathick maps.
INFO | 01/06/26 15:07:43 | nispace.datasets: Loading integrated collection 'Adult' for dataset 'enigmathick'.
INFO | 01/06/26 15:07:43 | nispace.datasets: Filtering maps by collection.
INFO | 01/06/26 15:07:43 | nispace.datasets: Loading data parcellated with 'DesikanKilliany'
ENIGMA thickness: 17 disorders x 68 cortical parcels
Disorders: [('dx-mdd_age-adult_pub-schmaal2017',), ('dx-adhd_age-allages_pub-hoogman2019',), ('dx-adhd_age-adult_pub-hoogman2019',), ('dx-asd_pub-vanrooij2018',), ('dx-bd_age-adult_pub-hibar2018',), ('dx-scz_pub-vanerp2018',), ('dx-ocd_age-adult_pub-boedhoe2018',), ('dx-epilepsy_pub-whelan2018',), ('dx-epilepsy_subtype-gge_pub-whelan2018',), ('dx-epilepsy_subtype-ltle_pub-whelan2018',), ('dx-epilepsy_subtype-rtle_pub-whelan2018',), ('dx-22q_pub-sun2020',), ('dx-an_pub-walton2022',), ('dx-an_subtype-acAN_pub-walton2022',), ('dx-an_subtype-pwrAN_pub-walton2022',), ('dx-antisocial_pub-gao2024',), ('dx-pd_pub-laansma2021',)]
INFO | 01/06/26 15:07:43 | nispace.datasets: Loading rsn maps.
INFO | 01/06/26 15:07:43 | nispace.datasets: Loading data parcellated with 'DesikanKilliany'
PET: 14 maps x 68 parcels
Individual-level colocalization
The ENIGMA dataset gives us effect size maps for multiple disorders simultaneously. We can test all disorders at once by passing the full DataFrame as y.
[3]:
nsp = NiSpace(
x=pet_maps,
y=enigma_thick, # all 22 disorder maps as input
parcellation="DesikanKilliany",
seed=42,
n_proc=4
)
nsp.fit()
nsp.colocalize("spearman")
nsp.permute("maps", n_perm=1000)
nsp.correct_p()
colocs = nsp.get_colocalizations()
print(f"Result: {colocs.shape[0]} disorders x {colocs.shape[1]} receptors")
p_values = nsp.get_p_values()
print(f"Result: {p_values.shape[0]} x {p_values.shape[1]} receptors")
INFO | 01/06/26 15:07:43 | nispace.api: *** NiSpace.fit() - Data extraction and preparation. ***
INFO | 01/06/26 15:07:43 | nispace.core.parcellation: Building multi-space Parcellation for 'DesikanKilliany' from library.
INFO | 01/06/26 15:07:43 | nispace.core.parcellation: Available spaces: MNI152NLin2009cAsym, MNI152NLin6Asym, fsaverage, fsLR
INFO | 01/06/26 15:07:43 | nispace.core.parcellation: Parcellation 'DesikanKilliany': validation passed.
INFO | 01/06/26 15:07:43 | nispace.api: Checking input data for 'x' (should be, e.g., PET data):
INFO | 01/06/26 15:07:43 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
INFO | 01/06/26 15:07:43 | nispace.api: Got 'x' data for 14 x 68 parcels.
INFO | 01/06/26 15:07:43 | nispace.api: Checking input data for 'y' (should be, e.g., subject data):
INFO | 01/06/26 15:07:43 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
WARNING | 01/06/26 15:07:43 | nispace.io: Parcellated data contains nan values!
INFO | 01/06/26 15:07:43 | nispace.api: Got 'y' data for 17 x 68 parcels.
INFO | 01/06/26 15:07:43 | nispace.api: Z-standardizing 'X' data.
INFO | 01/06/26 15:07:43 | nispace.api: *** NiSpace.colocalize() - Estimating X & Y colocalizations. ***
INFO | 01/06/26 15:07:43 | nispace.api: Running 'spearman' colocalization.
INFO | 01/06/26 15:07:43 | nispace.api: Pre-ranking X and Y data.
Colocalizing (spearman, 4 proc): 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 17/17 [00:03<00:00, 4.81it/s]
INFO | 01/06/26 15:07:47 | nispace.api: *** NiSpace.permute() - Estimate exact non-parametric p values. ***
INFO | 01/06/26 15:07:47 | nispace.api: Permutation of: X maps.
INFO | 01/06/26 15:07:47 | nispace.api: Using default null method 'alexander_bloch' (parcellation null space: 'fsLR').
INFO | 01/06/26 15:07:47 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsLR'.
INFO | 01/06/26 15:07:47 | nispace.api: Will calculate p values for mean colocalization across Y maps. Set 'pooled_p=False' to compute p values for each Y map individually.
INFO | 01/06/26 15:07:47 | nispace.api: Loading observed colocalizations (method = 'spearman').
INFO | 01/06/26 15:07:47 | nispace.core.permute: No null maps found.
INFO | 01/06/26 15:07:47 | nispace.core.permute: Generating null maps (n = 1000, null_method = 'alexander_bloch').
INFO | 01/06/26 15:07:47 | nispace.nulls: Null map generation: Assuming n = 14 data vector(s) for n = 68 parcels.
INFO | 01/06/26 15:07:47 | nispace.nulls: Using provided precomputed spin matrix.
Spin null maps: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 14/14 [00:00<00:00, 229.93it/s]
INFO | 01/06/26 15:07:47 | nispace.nulls: Null data generation finished.
INFO | 01/06/26 15:07:47 | nispace.core.permute: Z-standardizing null maps.
Processing null arrays (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 1000.44it/s]
Null colocalizations (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 8190.83it/s]
INFO | 01/06/26 15:07:48 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'two'}).
Result: 17 disorders x 14 receptors
Result: 1 x 14 receptors
[4]:
# plot all disorders
nsp.plot(sort_by="p")
INFO | 01/06/26 15:07:48 | nispace.plotting: Significance annotation: 2/14 p_uncorrected < 0.05, 1/14 p_meffgalwey < 0.05
[4]:
(<Figure size 500x430 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization\n(permutation of $X\\ maps$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x16addc640>)
That is interesting in itself. But we might now want to know how each individual ENIGMA map colocalizes with each individual resting-state network.
To achieve that, we can set a flag in NiSpace.permute(), which deactivates the group-level p value computation.
[5]:
nsp.colocalize("spearman")
nsp.permute("maps", n_perm=1000, p_from_average_y_coloc=False)
nsp.correct_p()
# both the colocalization and the p value dataframes will now be a n_reference x n_enigma dataframe
colocs = nsp.get_colocalizations()
print(f"Colocalizations: {colocs.shape[0]} disorders x {colocs.shape[1]} receptors")
p_values = nsp.get_p_values()
pc_values = nsp.get_corrected_p_values()
print(f"P-Values: {p_values.shape[0]} disorders x {p_values.shape[1]} receptors")
Colocalizing (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 17/17 [00:00<00:00, 1535.42it/s]
INFO | 01/06/26 15:07:48 | nispace.core.permute: Found existing null maps.
INFO | 01/06/26 15:07:48 | nispace.core.permute: Z-standardizing null maps.
Processing null arrays (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 9310.72it/s]
Null colocalizations (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 8353.24it/s]
INFO | 01/06/26 15:07:49 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'two'}).
Colocalizations: 17 disorders x 14 receptors
P-Values: 17 disorders x 14 receptors
A heatmap is a typical means to visualize this. For now, we just use seaborn.heatmap() and add the p values as annotation.
[6]:
import seaborn as sns
sns.heatmap(
colocs.T,
center=0,
square=True,
annot=((p_values.T < 0.05).astype(int) + (pc_values.T.astype(int) < 0.05)) \
.replace({0: "", 1: "☆", 2: "★"}),
fmt="s"
)
[6]:
<Axes: xlabel='map', ylabel='map'>
Individual-level deviation colocalization
Except for the last, the analyses so far have produced group-level results: one colocalization value per disorder or condition. But we often may want to compute colocalization for each subject individually.
The "zscore(a,b)" transform in transform_y() produces one deviation map per subject in group A (patients). We can then colocalize each map separately and look at the distribution across subjects.
This is particularly useful for stratifying patients by their colocalization profile (e.g., identifying subgroups with different neurotransmitter involvement).
We return to the anorexia nervosa example dataset here — as a reminder, it is simulated and not intended for scientific use.
[7]:
from nispace.datasets import fetch_example
an_data = fetch_example("anorexianervosa", parcellation="Schaefer200")
groups = an_data.index.str.extract(r'(AN|HC)$')[0].values
pet_s200 = fetch_reference("pet", parcellation="Schaefer200",
collection="UniqueTracers", print_references=False)
nsp_indiv = NiSpace(
x=pet_s200,
y=an_data,
parcellation="Schaefer200",
seed=42,
n_proc=4
)
nsp_indiv.fit()
# compute z-scores: each AN patient mapped relative to HC mean and SD
nsp_indiv.transform_y("zscore(a,b)", groups=groups)
print(f"Individual z-score maps: {nsp_indiv.get_y().shape}")
INFO | 01/06/26 15:07:49 | nispace.datasets: Loading example dataset: 'anorexianervosa', parcellated with: Schaefer200Parcels7Networks.
INFO | 01/06/26 15:07:49 | nispace.datasets: Loading pet maps.
INFO | 01/06/26 15:07:49 | nispace.datasets: Loading integrated collection 'UniqueTracers' for dataset 'pet'.
INFO | 01/06/26 15:07:49 | nispace.datasets: Filtering maps by collection.
INFO | 01/06/26 15:07:49 | nispace.datasets: Loading data parcellated with 'Schaefer200Parcels7Networks'
INFO | 01/06/26 15:07:49 | nispace.core.parcellation: Building multi-space Parcellation for 'Schaefer200Parcels7Networks' from library.
INFO | 01/06/26 15:07:49 | nispace.core.parcellation: Available spaces: MNI152NLin2009cAsym, MNI152NLin6Asym, fsaverage, fsLR
INFO | 01/06/26 15:07:49 | nispace.core.parcellation: Parcellation 'Schaefer200Parcels7Networks': validation passed.
INFO | 01/06/26 15:07:49 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
WARNING | 01/06/26 15:07:49 | nispace.io: Parcellated data contains nan values!
INFO | 01/06/26 15:07:49 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
Individual z-score maps: (50, 200)
[8]:
# colocalize each subject's z-score map with PET maps
nsp_indiv.colocalize("spearman")
# result: one row per AN subject, one column per receptor
indiv_colocs = nsp_indiv.get_colocalizations()
print(f"Individual colocalizations: {indiv_colocs.shape}")
indiv_colocs.head(5)
Colocalizing (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 50/50 [00:00<00:00, 2738.83it/s]
Individual colocalizations: (50, 29)
[8]:
| set | General | Immunity | Glutamate | GABA | ... | Serotonin | Noradrenaline/Acetylcholine | Opioids/Endocannabinoids | Histamine | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| map | target-CMRglu_tracer-fdg_n-20_dx-hc_pub-castrillon2023 | target-rCPS_tracer-leucine_n-42_dx-hc_pub-smith2023 | target-SV2A_tracer-ucbj_n-76_dx-hc_pub-finnema2016 | target-HDAC_tracer-martinostat_n-8_dx-hc_pub-wey2016 | target-VMAT2_tracer-dtbz_n-76_dx-hc_pub-larsen2020 | target-TSPO_tracer-pbr28_n-6_dx-hc_pub-lois2018 | target-COX1_tracer-ps13_n-11_dx-hc_pub-kim2020 | target-mGluR5_tracer-abp688_n-73_dx-hc_pub-smart2019 | target-NMDA_tracer-ge179_n-29_dx-hc_pub-galovic2021 | target-GABAa5_tracer-ro154513_n-10_dx-hc_pub-lukow2022 | ... | target-5HT6_tracer-gsk215083_n-30_dx-hc_pub-radhakrishnan2018 | target-5HTT_tracer-dasb_n-18_dx-hc_pub-savli2012 | target-NET_tracer-mrb_n-10_dx-hc_pub-hesse2017 | target-A4B2_tracer-flubatine_n-30_dx-hc_pub-hillmer2016 | target-M1_tracer-lsn3172176_n-24_dx-hc_pub-naganawa2020 | target-VAChT_tracer-feobv_n-18_dx-hc_pub-aghourian2017 | target-MOR_tracer-carfentanil_n-204_dx-hc_pub-kantonen2020 | target-KOR_tracer-ly2795050_n-28_dx-hc_pub-vijay2018 | target-CB1_tracer-omar_n-77_dx-hc_pub-normandin2015 | target-H3_tracer-gsk189254_n-8_dx-hc_pub-gallezot2017 |
| sub-001AN | 0.017047 | 0.063481 | 0.077512 | 0.022713 | 0.000522 | -0.051870 | 0.031316 | 0.148510 | 0.033367 | 0.015989 | ... | 0.069499 | -0.024101 | 0.138824 | 0.103126 | 0.079042 | 0.082800 | 0.028306 | 0.148780 | 0.002946 | -0.026949 |
| sub-002AN | 0.050685 | 0.028544 | 0.049336 | 0.059077 | 0.010109 | 0.077949 | -0.063088 | 0.063228 | 0.019509 | 0.024376 | ... | 0.106034 | -0.044316 | 0.008265 | 0.082261 | 0.089020 | 0.044310 | 0.047142 | 0.109717 | 0.062302 | 0.077379 |
| sub-003AN | 0.049166 | 0.004716 | -0.015789 | 0.042196 | -0.058407 | -0.095477 | 0.023831 | 0.051145 | -0.006990 | -0.146041 | ... | -0.019279 | -0.117699 | 0.021439 | -0.075802 | 0.021563 | -0.054418 | -0.077310 | -0.029879 | -0.072018 | 0.011837 |
| sub-004AN | -0.063293 | -0.073066 | -0.114253 | -0.047778 | -0.121368 | -0.006215 | -0.059130 | -0.078576 | -0.080251 | -0.033082 | ... | 0.032534 | -0.075188 | -0.018891 | -0.018200 | -0.073574 | 0.018696 | 0.053493 | 0.072137 | 0.081855 | -0.017590 |
| sub-005AN | -0.048554 | -0.017159 | -0.094473 | -0.034040 | -0.040044 | -0.007892 | -0.018086 | -0.036658 | -0.035953 | -0.065498 | ... | -0.004646 | -0.065861 | -0.006555 | -0.031980 | -0.044842 | -0.027111 | -0.076432 | 0.027479 | 0.015603 | 0.016296 |
5 rows × 29 columns
[9]:
# for permutation testing with individual maps, we permute group labels
# p values are computed for the MEAN colocalization across subjects by default
# -> compare to the section above
nsp_indiv.permute("groups", groups=groups, n_perm=1000)
nsp_indiv.correct_p()
p_indiv = nsp_indiv.get_p_values()
print(f"P-values shape: {p_indiv.shape}")
print(p_indiv.T.sort_values(by=p_indiv.index[0]).head(10))
INFO | 01/06/26 15:07:49 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsLR'.
Permuting groups (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 868745.65it/s]
Null transformations (spearman, 4 proc): 100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 14698.29it/s]
Processing null arrays (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 7053.77it/s]
Null colocalizations (spearman, 4 proc): 100%|████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:01<00:00, 972.59it/s]
INFO | 01/06/26 15:07:51 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'two'}).
P-values shape: (1, 29)
mean
set map
Serotonin target-5HTT_tracer-dasb_n-18_dx-hc_pub-savli2012 0.001
target-5HT1a_tracer-way100635_n-35_dx-hc_pub-sa... 0.001
GABA target-GABAa5_tracer-ro154513_n-10_dx-hc_pub-lu... 0.001
Dopamine target-FDOPA_tracer-fluorodopa_n-12_dx-hc_pub-g... 0.001
General target-VMAT2_tracer-dtbz_n-76_dx-hc_pub-larsen2020 0.002
Dopamine target-D1_tracer-sch23390_n-13_dx-hc_pub-kaller... 0.002
Opioids/Endocannabinoids target-MOR_tracer-carfentanil_n-204_dx-hc_pub-k... 0.008
Immunity target-TSPO_tracer-pbr28_n-6_dx-hc_pub-lois2018 0.012
Dopamine target-D23_tracer-flb457_n-55_dx-hc_pub-sandieg... 0.012
Opioids/Endocannabinoids target-CB1_tracer-omar_n-77_dx-hc_pub-normandin... 0.034
[10]:
# plot
nsp_indiv.plot()
INFO | 01/06/26 15:07:52 | nispace.plotting: Significance annotation: 12/29 p_uncorrected < 0.05, 6/29 p_meffgalwey < 0.05
[10]:
(<Figure size 500x730 with 1 Axes>,
<Axes: title={'center': "$Spearman's\\ Rho$ colocalization after $Z\\ score$ transform\n(permutation of $Groups$)"}, xlabel='$Rho$'>,
<seaborn._core.plot.Plotter at 0x16c59ac10>)
Dimensionality reduction with reduce_x()
When you have many reference maps (30 PET tracers, thousands of genes), interpreting individual colocalizations can be difficult — and many maps are correlated with each other. reduce_x() summarizes the reference maps into a smaller set of components using PCA, ICA, or factor analysis.
After reduction, colocalize() operates on the components instead of individual maps.
[11]:
# reduce the PET maps to their principal components
nsp.reduce_x("pca", n_components=5) # keep 5 components
# colocalize on the reduced space
nsp.colocalize("spearman", X_reduction="pca")
nsp.permute("maps", n_perm=1000, X_reduction="pca")
pca_colocs = nsp.get_colocalizations(X_reduction="pca")
print(f"PCA colocalizations: {pca_colocs.shape[0]} disorders x {pca_colocs.shape[1]} components")
pca_colocs
INFO | 01/06/26 15:07:52 | nispace.core.reduce_x: Performing dimensionality reduction using pca (max components: 5, min EV: None).
INFO | 01/06/26 15:07:52 | nispace.core.reduce_x: Returning 5 principal component(s).
Colocalizing (spearman, 4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 17/17 [00:00<00:00, 1594.72it/s]
INFO | 01/06/26 15:07:52 | nispace.core.permute: Found existing null maps.
WARNING | 01/06/26 15:07:52 | nispace.core.permute: Not all X/Y variables in null maps. Will re-generate.
INFO | 01/06/26 15:07:52 | nispace.core.permute: Generating null maps (n = 1000, null_method = 'alexander_bloch').
INFO | 01/06/26 15:07:52 | nispace.nulls: Null map generation: Assuming n = 5 data vector(s) for n = 68 parcels.
INFO | 01/06/26 15:07:52 | nispace.nulls: Using provided precomputed spin matrix.
Spin null maps: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 220.08it/s]
INFO | 01/06/26 15:07:52 | nispace.nulls: Null data generation finished.
INFO | 01/06/26 15:07:52 | nispace.core.permute: Z-standardizing null maps.
Processing null arrays (4 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 9487.83it/s]
Null colocalizations (spearman, 4 proc): 100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 11323.65it/s]
INFO | 01/06/26 15:07:52 | nispace.core.permute: Calculating exact p-values (tails = {'rho': 'two'}).
PCA colocalizations: 17 disorders x 5 components
[11]:
| c0 | c1 | c2 | c3 | c4 | |
|---|---|---|---|---|---|
| map | |||||
| dx-mdd_age-adult_pub-schmaal2017 | 0.350335 | -0.381529 | -0.178854 | -0.356347 | -0.011331 |
| dx-adhd_age-allages_pub-hoogman2019 | -0.132182 | -0.057538 | 0.045820 | -0.319895 | 0.396888 |
| dx-adhd_age-adult_pub-hoogman2019 | -0.014370 | -0.526697 | -0.062857 | -0.445011 | 0.198560 |
| dx-asd_pub-vanrooij2018 | -0.310917 | 0.311928 | 0.475533 | -0.249393 | 0.087193 |
| dx-bd_age-adult_pub-hibar2018 | 0.180692 | -0.700799 | 0.065560 | 0.539223 | 0.002533 |
| dx-scz_pub-vanerp2018 | -0.054100 | -0.472228 | 0.179924 | 0.260594 | -0.159836 |
| dx-ocd_age-adult_pub-boedhoe2018 | 0.471912 | -0.214527 | -0.215989 | 0.270299 | 0.046994 |
| dx-epilepsy_pub-whelan2018 | -0.191002 | 0.080550 | 0.122663 | 0.522908 | 0.284856 |
| dx-epilepsy_subtype-gge_pub-whelan2018 | -0.044712 | 0.110665 | 0.224301 | 0.256793 | 0.414935 |
| dx-epilepsy_subtype-ltle_pub-whelan2018 | -0.248365 | 0.134195 | 0.039337 | 0.534235 | 0.306862 |
| dx-epilepsy_subtype-rtle_pub-whelan2018 | -0.472573 | 0.228192 | 0.047887 | 0.492719 | 0.115260 |
| dx-22q_pub-sun2020 | 0.137077 | 0.161603 | 0.192222 | -0.435269 | -0.060933 |
| dx-an_pub-walton2022 | -0.162079 | -0.120526 | -0.237759 | 0.802155 | 0.065844 |
| dx-an_subtype-acAN_pub-walton2022 | -0.060853 | 0.031242 | -0.094012 | 0.768742 | -0.226304 |
| dx-an_subtype-pwrAN_pub-walton2022 | -0.221277 | -0.236615 | -0.235340 | 0.756421 | 0.055607 |
| dx-antisocial_pub-gao2024 | -0.104394 | -0.192869 | 0.522142 | 0.132958 | 0.064234 |
| dx-pd_pub-laansma2021 | 0.123394 | 0.376470 | -0.037572 | 0.339391 | 0.208653 |
[12]:
nsp.plot(X_reduction="pca")
INFO | 01/06/26 15:07:53 | nispace.plotting: Significance annotation: 1/5 p_uncorrected < 0.05, 0/5 p_meffgalwey < 0.05 (no correction applied)
[12]:
(<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 0x16e177e80>)
Alternative colocalization methods
Spearman correlation is the default, but NiSpace supports several other methods. Each has different assumptions and use cases.
Importantly, methods might return multiple different colocalization metrics. For example, a linear regression model ("mlr") already returns both beta coefficients (one for each X map, as for correlations) and the R^2 value (one across X maps). Each of these colocalization statistics will furthermore have an associated p values after .permute() has been run.
[13]:
# set up a simple analysis for comparison
nsp_methods = NiSpace(
x=pet_maps,
y=enigma_thick.loc[["dx-bd_age-adult_pub-hibar2018"]], # just bipolar disorder for clarity
parcellation="DesikanKilliany",
seed=42
)
nsp_methods.fit()
# Pearson correlation
nsp_methods.colocalize("pearson")
pearson = nsp_methods.get_colocalizations()
print("Pearson:", pearson.values.squeeze())
print("")
# Multiple linear regression (MLR)
nsp_methods.colocalize("mlr")
mlr = nsp_methods.get_colocalizations()
print("MLR beta:", mlr["beta"].values.squeeze())
print("MLR R2:", mlr["r2"].values.squeeze())
print("")
# Partial least squares (PLS)
nsp_methods.colocalize("pls", n_components=3)
pls = nsp_methods.get_colocalizations()
print("PLS beta:", pls["beta"].values.squeeze())
print("PLS R2:", pls["r2"].values.squeeze())
print("")
# Dominance analysis (takes the longest)
nsp_methods.colocalize("dominance")
dom = nsp_methods.get_colocalizations()
print("Dominance: sum (matches MLR R2) :", dom["sum"].values.squeeze())
print("Dominance: total (sum of total is MLR R2):", dom["total"].values.squeeze())
print("Dominance: individual (like a leave-one-out MLR R2):", dom["individual"].values.squeeze())
print("Dominance: relative (fraction of sum R2):", dom["relative"].values.squeeze())
INFO | 01/06/26 15:07:53 | nispace.core.parcellation: Building multi-space Parcellation for 'DesikanKilliany' from library.
INFO | 01/06/26 15:07:53 | nispace.core.parcellation: Available spaces: MNI152NLin2009cAsym, MNI152NLin6Asym, fsaverage, fsLR
INFO | 01/06/26 15:07:53 | nispace.core.parcellation: Parcellation 'DesikanKilliany': validation passed.
INFO | 01/06/26 15:07:53 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
INFO | 01/06/26 15:07:53 | nispace.io: Input type: DataFrame, assuming parcellated data with shape (n_files/subjects/etc, n_parcels).
Colocalizing (pearson, 1 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 311.80it/s]
Pearson: [ 0.09379738 -0.12041875 -0.32349086 -0.22995271 -0.70154136 -0.3893741
0.25937384 0.10179116 0.20780352 0.17480677 0.14520817 0.13494447
0.1137092 0.29869333]
Colocalizing (mlr, 1 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 121.49it/s]
MLR beta: [ 0.00266048 -0.01679382 -0.02431268 -0.01540803 -0.02362837 -0.00963058
0.00947228 0.00963644 0.00542823 0.02405527 0.00453575 0.00743604
0.00379609 0.00881169]
MLR R2: 0.46437818
Colocalizing (pls, 1 proc): 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 120.95it/s]
PLS beta: [ 0.0021201 -0.01162152 -0.01987142 -0.01660991 -0.024217 -0.00883197
0.01095411 0.00524392 0.0065681 0.02115439 0.00681146 0.00787176
0.00419393 0.01316884]
PLS R2: 0.5715146
Colocalizing (dominance, 1 proc): 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 3.70it/s]
Dominance: sum (matches MLR R2) : 0.46437818
Dominance: total (sum of total is MLR R2): [1.3349009e-04 8.5304175e-03 5.7703033e-02 3.6930375e-02 1.8202354e-01
5.0407633e-02 2.7346514e-02 8.9180330e-04 1.0367648e-02 4.3500487e-02
4.6723862e-03 1.7571739e-03 2.8868834e-04 3.9825007e-02]
Dominance: individual (like a leave-one-out MLR R2): [-0.00627205 -0.00057247 0.08408586 0.03669057 0.35684335 0.12443971
0.05019241 -0.00470505 0.02745323 0.01524752 0.00595633 0.00311236
-0.0021382 0.07029137]
Dominance: relative (fraction of sum R2): [2.87459872e-04 1.83695480e-02 1.24258704e-01 7.95264989e-02
3.91972631e-01 1.08548664e-01 5.88884540e-02 1.92042463e-03
2.23258715e-02 9.36746970e-02 1.00615975e-02 3.78392870e-03
6.21666433e-04 8.57598558e-02]
If we plot multi-outcome colocalization results, we will automatically get one plot for each metric. E.g., for the quite detailled dominance output:
[14]:
# first: run the proper permutation; will take some time for dominance analysis
nsp_methods.permute("maps", n_perm=1000, n_proc=4)
nsp_methods.correct_p()
# show
nsp_methods.plot()
INFO | 01/06/26 15:07:53 | nispace.core.parcellation: Lazy-loading parcellation image for space 'fsLR'.
INFO | 01/06/26 15:07:53 | nispace.core.permute: No null maps found.
INFO | 01/06/26 15:07:53 | nispace.core.permute: Generating null maps (n = 1000, null_method = 'alexander_bloch').
INFO | 01/06/26 15:07:53 | nispace.nulls: Null map generation: Assuming n = 14 data vector(s) for n = 68 parcels.
INFO | 01/06/26 15:07:53 | nispace.nulls: Using provided precomputed spin matrix.
Spin null maps: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 14/14 [00:00<00:00, 257.90it/s]
INFO | 01/06/26 15:07:53 | nispace.nulls: Null data generation finished.
INFO | 01/06/26 15:07:53 | nispace.core.permute: Z-standardizing null maps.
Null colocalizations (dominance, 4 proc): 100%|████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [01:03<00:00, 15.80it/s]
INFO | 01/06/26 15:08:57 | nispace.core.permute: Calculating exact p-values (tails = {'sum': 'upper', 'total': 'upper', 'individual': 'upper', 'relative': 'upper'}).
INFO | 01/06/26 15:08:57 | nispace.plotting: Significance annotation: 0/1 p_uncorrected < 0.05, 0/1 p_meffgalwey < 0.05
INFO | 01/06/26 15:08:57 | nispace.plotting: Significance annotation: 1/14 p_uncorrected < 0.05, 1/14 p_meffgalwey < 0.05
INFO | 01/06/26 15:08:57 | nispace.plotting: Significance annotation: 2/14 p_uncorrected < 0.05, 1/14 p_meffgalwey < 0.05
INFO | 01/06/26 15:08:58 | nispace.plotting: Significance annotation: 0/14 p_uncorrected < 0.05, 0/14 p_meffgalwey < 0.05
[14]:
{'sum': (<Figure size 170x500 with 1 Axes>,
<Axes: title={'center': '$Dominance\\ Analysis$ colocalization\n(permutation of $X\\ maps$)'}, ylabel='$sum$'>,
<seaborn._core.plot.Plotter at 0x168af05b0>),
'total': (<Figure size 500x430 with 1 Axes>,
<Axes: title={'center': '$Dominance\\ Analysis$ colocalization\n(permutation of $X\\ maps$)'}, xlabel='$Total\\ R^2$'>,
<seaborn._core.plot.Plotter at 0x17acf1940>),
'individual': (<Figure size 500x430 with 1 Axes>,
<Axes: title={'center': '$Dominance\\ Analysis$ colocalization\n(permutation of $X\\ maps$)'}, xlabel='$Individual\\ R^2$'>,
<seaborn._core.plot.Plotter at 0x17ae17d90>),
'relative': (<Figure size 500x430 with 1 Axes>,
<Axes: title={'center': '$Dominance\\ Analysis$ colocalization\n(permutation of $X\\ maps$)'}, xlabel='$relative$'>,
<seaborn._core.plot.Plotter at 0x17b074c40>)}
Summary
Feature |
How to use |
|---|---|
Individual-level colocalization |
|
Individual-level deviation colocalization |
|
Dimensionality reduction |
|
Alternative colocalization methods |
|
This is the last notebook in the introduction series. For more, see the API reference and the Example notebooks.