Classification Example
This walkthrough runs a complete CPM analysis for a binary target
(classification) on simulated data. It mirrors the
regression example — the workflow is identical; only the target,
edge statistic, cross-validation splitter, and reported metrics differ. The script
below is the runnable file examples/classification_quickstart.py.
The full script
"""
Quickstart: Connectome-based Predictive Modeling for a *binary classification*
target.
This mirrors ``regression_quickstart.py`` but predicts a 0/1 class label instead
of a continuous score. The only real differences are:
* the target ``y`` is binary,
* a stratified cross-validation splitter, and
* classification metrics in the report (accuracy, balanced accuracy, F1, AUC).
Run it with:
poetry run python examples/classification_quickstart.py
"""
import numpy as np
from sklearn.model_selection import StratifiedKFold
from cccpm import CPMAnalysis, UnivariateEdgeSelection, PThreshold
from cccpm.simulation.simulate_sem import simulate_data_given_kappa
# ---------------------------------------------------------------------------
# 1. Get some data
# ---------------------------------------------------------------------------
# X : connectome edges, shape (n_samples, n_features)
# y : the binary outcome (0/1), shape (n_samples,)
# covariates : nuisance variables to control for, shape (n_samples, n_covariates)
#
# The SEM-based simulator generates a *continuous* outcome with a known amount of
# confounding (see regression_quickstart.py). ``kappa`` is the fraction of the
# apparent brain–outcome R² that is actually confound-driven. We then binarise the
# continuous outcome at its median to obtain a balanced two-class label — so the
# confound is still baked into the connectome and confound control matters here too.
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, covariates = sim["X"], sim["Z"]
y = (sim["y"].ravel() > np.median(sim["y"])).astype(float) # median split → 0/1
# ---------------------------------------------------------------------------
# 2. Configure edge selection
# ---------------------------------------------------------------------------
# A binary 0/1 target through the Pearson path *is* the point-biserial
# correlation, so there is no separate statistic to pick. Confound control works
# exactly as in the regression quickstart: `selection_input` here, `model_input`
# on CPMAnalysis.
edge_selection = UnivariateEdgeSelection(
selection_statistic="pearson",
selection_input="residualized",
edge_selection=[PThreshold(threshold=[0.05], correction=[None])],
)
# ---------------------------------------------------------------------------
# 3. Configure and run the analysis
# ---------------------------------------------------------------------------
cpm = CPMAnalysis(
results_directory="./results/classification_quickstart",
task_type="classification", # or leave as None to auto-detect
cv=StratifiedKFold(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
)
cpm.run(X=X, y=y, covariates=covariates)
# ---------------------------------------------------------------------------
# 4. Inspect the results
# ---------------------------------------------------------------------------
# Everything is written under the results_directory, including:
# - cv_results_summary.csv : metrics (accuracy, balanced accuracy, F1, ROC AUC)
# - p_values.csv : permutation-based significance
# - cv_predictions.csv : out-of-sample predictions per subject
# - report.html : a full, human-readable HTML report
print("Done. Open ./results/classification_quickstart/report.html to explore the results.")
What differs from regression
| Aspect | Regression | Classification |
|---|---|---|
Target y |
continuous | binary (0/1) |
task_type |
"regression" |
"classification" (or None to auto-detect) |
selection_statistic |
pearson / spearman |
pearson — point-biserial correlation is Pearson against a 0/1 target, so there is nothing extra to select |
| Cross-validation | KFold |
StratifiedKFold (keeps class balance across folds) |
| Metrics | explained variance, Pearson r, MSE, MAE | accuracy, balanced accuracy, F1, ROC AUC |
increment reported for |
explained variance, MSE, MAE (not Pearson r) | accuracy, balanced accuracy, ROC AUC (not F1) |
Tip
For imbalanced classes, prefer balanced accuracy and ROC AUC over plain accuracy. CCCPM reports all four so you can compare.
Step by step
The steps are the same as in the regression example: prepare your
X / y / covariates, configure UnivariateEdgeSelection, construct CPMAnalysis,
and call run. The target here is binary, so we use a StratifiedKFold splitter and
set task_type="classification". Confound control works exactly as in regression —
selection_input for edge selection, model_input for the connectome the models
consume.
Results
Everything is written under results_directory, including report.html with
classification metrics. See Interpreting Results for a
full guide.
See also
- Regression example — the same workflow for a continuous target.
- Interpreting Results — what every output means.