Skip to content

Repository files navigation

msnpip — Morphometric Similarity Networks and Transcriptomics Pipeline

License: MITStatus: maintenance

🚧 Under maintenance

This repository is actively being reworked (v2 rebuild in progress). The documentation below describes the intended v2 design; some interfaces, outputs, and instructions may change or break without notice while maintenance is ongoing. Treat it as a preview, not a stable release.

📌 Thesis snapshot

The exact version used for the MSc thesis is preserved on branch v1-april (tag v1.0-thesis, commit cc8c776). main is the current, improved v2.

A modular Python pipeline that builds Morphometric Similarity Networks (MSN) from FreeSurfer cortical data, contrasts node strength between groups, and links the regional patterns to gene expression from the Allen Human Brain Atlas via PLS and enrichment (ensemble-GCEA + GSEA). The transcriptomics engine is the Imaging Transcriptomics Toolbox (v2.0.0), pinned to a fixed commit.

Pipeline Overview


What it does

LOAD → VALIDATE → MSN → CONTRAST → (CORRELATION) → TRANSCRIPTOMICS → FIGURES → REPORT
  • MSN is a whole-cortex network: each region's 5 morphometric features (SurfArea, GrayVol, ThickAvg, MeanCurv, GausCurv) are z-scored within subject (robust modified z-score), and inter-regional similarity is the distance kernel S = 1 / (1 + d / n_metrics), where d is the Euclidean distance between the two regions' standardized feature vectors. Node strength is the sum (default) or mean of a region's similarity edges. Both hemispheres are always used upstream.
  • Group contrast per region (β / t / Cohen's d), with covariates of your choosing; site/scanner are always one-hot encoded.
  • Transcriptomics runs inside the pinned engine with the vasa surface-spin null. By default a failed surface spin falls back (auto → shuffle) with a warning; the resolved null is recorded and flagged on the report cover. Set allow_null_fallback=False to hard-fail instead.
  • No pickle anywhere: outputs are CSV / Parquet / NPZ / JSON / PNG / PDF only.

Installation

# Python 3.12 recommended. The engine installs from a pinned git commit, so git is required.
pip install -e .# add [dev] for the test/lint toolchain: pip install -e ".[dev]"# For a reproducible, known-good dependency set (the versions the suite is verified against):
pip install -e ".[dev]" -c constraints.txt

Verify the engine is wired up:

importimaging_transcriptomicsasimtassertany(a.id=="dk"forainimt.list_atlases())

The first run that needs cortical surfaces will fetch the neuromaps fsaverage meshes; in Docker these are baked at build time (see below).


Quick start

From FreeSurfer data

msnpip full \
--input /path/to/freesurfer_subjects/ \
--demographics demographics.csv \
--output out/ \
--group-col group --case FTD --control HC \
--predictors age sex tiv \
--atlas dk --hemisphere left --regions cort \
--method pls --ncomp 1 --n-perm 10000 \
--enrichment ensemble gsea

From a pre-merged DataFrame

msnpip full \
--dataframe merged.csv \
--output out/ \
--group-col group --case FTD --control HC \
--predictors age sex tiv --exclude-covariate age \
--correlate-with age --corr-scope global \
--atlas dk --method pls --ncomp 1 --n-perm 1000 --enrichment ensemble --seed 1234

Python API

frompathlibimportPathfrommsnpip.configimportIOConfig, GLMConfig, EngineConfig, PipelineConfigfrommsnpip.pipelineimportrun_pipelinecfg=PipelineConfig(
io=IOConfig(dataframe=Path("merged.csv")),
output=Path("out/"),
group_col="group", case="FTD", control="HC",
glm=GLMConfig(predictors=("age", "sex", "tiv")),
engine=EngineConfig(methods=("pls", "corr"), n_components=1, n_permutations=10000),
)
run_pipeline(cfg)

Resume / partial runs

# Run only part of the pipeline:
msnpip full ... --stop-stage MSN
msnpip full ... --start-stage TRANSCRIPTOMICS # reuses persisted earlier stages# Resume from a previous run's persisted strength maps:
msnpip from-strength --output out/ --case FTD --control HC --predictors age sex tiv

Helpers: msnpip list-atlases, msnpip list-genesets.


Input data format

FreeSurfer directory layout

freesurfer_subjects/
├── sub-001/stats/{lh,rh}.aparc.stats
├── sub-002/stats/{lh,rh}.aparc.stats
└── ...

Extracted metrics: SurfArea, GrayVol, ThickAvg, MeanCurv, GausCurv for the Desikan–Killiany cortical regions (34 per hemisphere).

Demographics / merged CSV

Roles are auto-detected by token matching (so subject_id is found, but region columns like lh_middletemporal_* are never mistaken for an id):

RoleExample column names
idsubject_id, participant_id, id
groupgroup, diagnosis, dx
ageage
sexsex, gender
tivtiv, icv
sitesite, scanner

IDs are matched exactly after whitespace strippingsub-001 and sub-1 are distinct. Feature columns follow {hemisphere}_{region}_{metric}, e.g. lh_superiorfrontal_ThickAvg.


Output tree

A curated, flat set — CSVs, a plots/ folder, and one report.pdf (<tag> is <case>_vs_<ctrl>). The verbose engine bundle is staged to a temporary .engine/ folder, curated into these files, then deleted — no manifest.json, no pickle.

out/
merged_dataset.csv validated, merged input table
strength_maps.csv per-subject node strength per region
mean_msn_per_group.csv group-mean node strength per region
case_control_difference_maps.csv per-contrast regional contrast map
<tag>_region_stats.csv per-region beta/t/cohen_d/p/fdr
<tag>_pls.csv <tag>_pls_summary.csv PLS gene results + component variance
<tag>_corr.csv correlation gene results (only if --method corr)
<tag>_enrichment.csv enrichment terms per backend × gene set
plots/ violin, t-value bars, surfaces, matrices, enrichment
report.pdf assembled A4-portrait report

See docs/outputs.md for a column-by-column reference and docs/tutorial.md for a runnable first run on synthetic data.


Docker

docker build -f docker/Dockerfile -t msnpip:2.0 .
docker run --rm -v "$PWD/data:/data:ro" -v "$PWD/out:/out" msnpip:2.0 \
full --dataframe /data/merged.csv --output /out \
--group-col group --case FTD --control HC \
--predictors age sex tiv --method pls --ncomp 1 \
--n-perm 1000 --enrichment ensemble --seed 1234

The image bakes the neuromaps fsaverage cache so cortical plots and the spin null work offline.


Locked methodological decisions

ItemValue
Null modelvasa surface spin; fallback-with-warning by default (allow_null_fallback=False to hard-fail)
MSN5 features, within-subject robust z-score, distance kernel 1/(1+d/n), sum strength, both hemispheres
Contrast statisticbeta (default), t, or cohen_d
Enrichmentensemble primary + gsea secondary
ID matchingexact after whitespace strip
Persistenceno pickle — CSV/Parquet/NPZ/JSON only
Site covariatealways one-hot
Defaultsatlas dk, engine hemisphere left, regions cort, n-perm 10,000

New here? Start with docs/tutorial.md (a runnable first run) and docs/outputs.md (what each output file means). See docs/statistics.md and docs/engine_contract.md for methods, and docs/adding_an_atlas.md to extend beyond DK.


License

MIT — see LICENSE.

About

A pipeline for integrating neuroimaging data with transcriptomic analysis using morphometric similarity networks.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages