Regression Example
This walkthrough runs a complete connectome-based predictive modeling (CPM)
analysis for a continuous target (regression) on simulated data. The script
below is the runnable file examples/regression_quickstart.py — copy it and swap in
your own data.
The full script
"""
Quickstart: Connectome-based Predictive Modeling for a *regression* target.
This script runs a complete CPM analysis on simulated data in a few lines:
it selects predictive edges, fits the models inside a cross-validation loop,
assesses significance with permutation testing, and writes an HTML report.
Run it with:
poetry run python examples/regression_quickstart.py
The companion script ``classification_quickstart.py`` shows the same workflow
for a binary target.
"""
import numpy as np
from sklearn.model_selection import KFold
from cccpm import CPMAnalysis, UnivariateEdgeSelection, PThreshold
from cccpm.simulation.simulate_sem import simulate_data_given_kappa
# ---------------------------------------------------------------------------
# 1. Get some data
# ---------------------------------------------------------------------------
# In a real study these would be your own arrays:
# X : connectome edges, shape (n_samples, n_features)
# y : the (continuous) outcome you want to predict, shape (n_samples,)
# covariates : nuisance variables to control for (e.g. age, motion, sex),
# shape (n_samples, n_covariates)
#
# Here we use the SEM-based simulator, which builds data with a *known* ground
# truth: a latent confound Z is a common cause of both the connectome and the
# outcome. ``kappa`` is the fraction of the naive brain–outcome R² that is
# actually confound-driven, so ``kappa=0.3`` means 30% of the apparent signal is
# spurious and confound control genuinely matters.
sim = simulate_data_given_kappa(
R2_X_y=0.4, # naive R²(y ~ X): apparent brain–outcome strength
kappa=0.3, # 30% of that R² is driven by the confound
n_features=4950, # a 100-node connectome (100*99/2 = 4950 edges),
# matching the built-in Schaefer100 atlas used below
n_features_informative=40, # "mixed" edges: real signal + confound leakage
n_pure_signal_features=20, # edges tied to y but NOT the confound
n_confound_only_features=20, # edges tied to y ONLY through the confound
n_confounds=2,
n_samples=200,
random_state=42,
)
X, y, covariates = sim["X"], sim["y"], sim["Z"]
# The simulator records the analytic ground truth, so we know what "good"
# deconfounded performance should look like before we even run the model.
info = sim["info"]
print(f"Naive R²(y~X) : {info['R2_X_y']:.2f} (inflated by the confound)")
print(f"True R²(y~X | Z) : {info['R2_X_y_given_Z']:.2f} (what we hope to recover)")
# ---------------------------------------------------------------------------
# 2. Configure edge selection
# ---------------------------------------------------------------------------
# Pick which edges enter the model by correlating each edge with the target and
# keeping those below a p-value threshold. Use 'pearson_partial' instead of
# 'pearson' to control for the covariates already *during* edge selection.
edge_selection = UnivariateEdgeSelection(
edge_statistic="pearson",
edge_selection=[PThreshold(threshold=[0.05], correction=[None])],
)
# ---------------------------------------------------------------------------
# 3. Configure and run the analysis
# ---------------------------------------------------------------------------
cpm = CPMAnalysis(
results_directory="./results/regression_quickstart",
task_type="regression", # or leave as None to auto-detect
cv=KFold(n_splits=10, shuffle=True, random_state=42),
edge_selection=edge_selection,
n_permutations=1000, # use 1000+ for a real analysis
atlas="Schaefer100-17", # built-in atlas → brain plots in the
# report; or pass a path to a custom
# CSV (region,x,y,z[,network]).
# See cccpm.atlases.list_atlases().
device="cpu", # "cuda" uses the GPU if available
edge_significance_method='nbs',
nbs_threshold=0.5
)
cpm.run(X=X, y=y, covariates=covariates)
# ---------------------------------------------------------------------------
# 4. Inspect the results
# ---------------------------------------------------------------------------
# Everything is written under the results_directory, including:
# - cv_results_summary.csv : performance metrics (Pearson r, MAE, MSE, ...)
# - p_values.csv : permutation-based significance
# - cv_predictions.csv : out-of-sample predictions per subject
# - report.html : a full, human-readable HTML report
#
# Because the connectome carries genuine confound leakage, compare the 'connectome'
# model against the 'residuals' model (which removes the covariates first): the gap
# between them is the confound inflation the SEM simulator built in on purpose.
print("Done. Open ./results/regression_quickstart/report.html to explore the results.")
Step by step
1. Data. CCCPM expects three aligned arrays:
X— the connectome, shape(n_samples, n_features), wheren_featuresare the upper-triangular edges of a symmetric node-by-node matrix (n_features = n_nodes * (n_nodes - 1) / 2). You can also pass full matrices of shape(n_samples, n_nodes, n_nodes)and CCCPM will vectorize them.y— the continuous outcome, shape(n_samples,).covariates— nuisance variables to control for (e.g. age, sex, motion).
The script fills these arrays with the SEM-based simulator, which bakes in a known amount of confounding so confound control actually matters — see Simulating Data for how it works and how to tune it.
2. Edge selection. UnivariateEdgeSelection correlates each edge with the target
and keeps edges below a p-value threshold. For regression, use pearson or
spearman; switch to the *_partial variants (e.g. pearson_partial) to control for
covariates during edge selection.
3. Run. CPMAnalysis performs the cross-validated fit, and — because
n_permutations > 0 — permutation testing for significance. For a real analysis,
prefer 1000+ permutations.
4. Results. Everything is written under results_directory, including
report.html. See Interpreting Results for a full guide
to the metrics, model variants, and output files.
See also
- Classification example — the same workflow for a binary target.
- Interpreting Results — what every output means.