Skip to main content Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill connectome-analysisThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... name connectome-analysis description Use this Skill for functional connectome analysis: connectivity matrices, graph metrics (clustering, path length, modularity), rich-club, hub detection.
tags ["neuroscience","connectomics","functional-connectivity","bctpy","nilearn"] version 1.0.0 authors [{"name":"Rosetta Skills Contributors","github":"@xjtulyc"}] license MIT platforms ["claude-code","codex","gemini-cli","cursor"] dependencies {"python":["bctpy>=0.5","nilearn>=0.10","networkx>=3.1","matplotlib>=3.7","numpy>=1.24","pandas>=2.0","scipy>=1.11"]} last_updated 2026-03-17 status stable
Connectome Analysis: Functional Brain Network Metrics
One-line summary : Compute functional connectivity matrices, graph-theoretic brain network metrics (clustering, path length, modularity, rich-club), and hub detection using bctpy and nilearn.
When to Use This Skill
When computing functional connectivity matrices from ROI time series
When characterizing brain network topology (small-world, scale-free)
When detecting functional communities/modules in the connectome
When identifying hub regions with high centrality
When computing rich-club coefficient for network resilience
When comparing connectivity patterns across groups (patients vs. controls)
Trigger keywords : functional connectivity, connectome, brain network, graph metrics, clustering coefficient, path length, modularity, rich-club, hub detection, bctpy, nilearn
Background & Key Concepts
Functional Connectivity
FC between ROIs $i$ and $j$ is typically:
$$
FC_{ij} = \text{Pearson}(x_i, x_j) = \frac{\text{cov}(x_i, x_j)}{\sigma_i \sigma_j}
$$
where $x_i$ is the BOLD time series of ROI $i$. The result is a symmetric $N \times N$ FC matrix.
Graph-Theoretic Metrics
Metric Definition Interpretation Clustering coefficient Fraction of triangles around a node Local segregation Characteristic path length Average shortest path Global integration Global efficiency Average inverse path length Robustness to damage Modularity Q Strength of community structure Functional specialization Rich-club coefficient Connectivity among high-degree hubs Core-periphery structure
Small-World Property
A network has small-world topology if:
$$
\sigma = \frac{C / C_{rand}}{L / L_{rand}} > 1
$$
where $C$ is clustering coefficient, $L$ is path length, subscript "rand" refers to random graph.
Environment Setup
Install Dependencies pip install bctpy>=0.5 nilearn>=0.10 networkx>=3.1 \
matplotlib>=3.7 numpy>=1.24 pandas>=2.0 scipy>=1.11
Verify Installation import bct
import nilearn
import networkx as nx
print (f"bctpy: {bct.__version__} " )
print (f"nilearn: {nilearn.__version__} " )
print (f"networkx: {nx.__version__} " )
Core Workflow
Step 1: Compute Functional Connectivity Matrix import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
from nilearn import plotting, datasets
def compute_fc_matrix (roi_timeseries, method="pearson" , threshold_pct=None ):
"""
Compute functional connectivity matrix from ROI time series.
Parameters
----------
roi_timeseries : ndarray, shape (n_timepoints, n_rois)
method : str
'pearson', 'partial' (partial correlation via inverse covariance)
threshold_pct : float or None
If float, keep top X% of connections (proportional thresholding)
Returns
-------
fc_matrix : ndarray, shape (n_rois, n_rois)
"""
n_tp, n_rois = roi_timeseries.shape
if method == "pearson" :
fc_matrix = np.corrcoef(roi_timeseries.T)
elif method == "partial" :
from sklearn.covariance import LedoitWolf
prec = LedoitWolf().fit(roi_timeseries).precision_
D = np.diag(np.sqrt(np.diag(prec)))
fc_matrix = -np.linalg.inv(D) @ prec @ np.linalg.inv(D)
np.fill_diagonal(fc_matrix, 1.0 )
else :
raise ValueError(f"Unknown method: {method} " )
np.fill_diagonal(fc_matrix, 0.0 )
if threshold_pct is not None :
k = int ((n_rois * (n_rois - 1 ) / 2 ) * threshold_pct / 100 )
upper = fc_matrix[np.triu_indices_from(fc_matrix, k=1 )]
threshold = np.sort(upper)[-k]
fc_matrix_thr = fc_matrix.copy()
fc_matrix_thr[fc_matrix < threshold] = 0
return fc_matrix_thr
return fc_matrix
rng = np.random.default_rng(42 )
n_tp = 200
n_rois = 50
labels = np.repeat(range (5 ), 10 )
ts = rng.standard_normal((n_tp, n_rois))
for g in range (5 ):
idx = np.where(labels == g)[0 ]
shared = rng.standard_normal(n_tp)
ts[:, idx] += 2 * shared[:, None ]
ts = (ts - ts.mean(axis=0 )) / ts.std(axis=0 )
fc = compute_fc_matrix(ts, method="pearson" )
fc_thr = compute_fc_matrix(ts, method="pearson" , threshold_pct=20 )
print (f"FC matrix: {fc.shape} , range [{fc.min ():.3 f} , {fc.max ():.3 f} ]" )
print (f"Mean positive FC: {fc[fc > 0 ].mean():.4 f} " )
fig, axes = plt.subplots(1 , 2 , figsize=(12 , 5 ))
im0 = axes[0 ].imshow(fc, cmap="RdBu_r" , vmin=-1 , vmax=1 )
plt.colorbar(im0, ax=axes[0 ], label="Pearson r" )
axes[0 ].set_title("Full FC Matrix" )
axes[0 ].set_xlabel("ROI" ); axes[0 ].set_ylabel("ROI" )
im1 = axes[1 ].imshow(fc_thr, cmap="RdBu_r" , vmin=-1 , vmax=1 )
plt.colorbar(im1, ax=axes[1 ], label="Pearson r" )
axes[1 ].set_title("Thresholded FC (top 20%)" )
plt.tight_layout()
plt.savefig("fc_matrix.png" , dpi=150 )
plt.show()
Step 2: Graph-Theoretic Metrics with BCT import bct
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
fc_pos = np.maximum(fc_thr, 0 )
fc_bin = (fc_pos > 0 ).astype(float )
print ("Computing graph-theoretic metrics..." )
C_nodal = bct.clustering_coef_bu(fc_bin)
L, eff = bct.charpath(bct.distance_bin(fc_bin))
Q_mod, ci = bct.modularity_und(fc_pos)
degree = fc_bin.sum (axis=1 )
bc = bct.betweenness_bin(fc_bin) / ((fc_bin.shape[0 ]-1 )*(fc_bin.shape[0 ]-2 ))
print (f"\nNetwork metrics:" )
print (f" Mean clustering coefficient: {C_nodal.mean():.4 f} " )
print (f" Characteristic path length: {L:.4 f} " )
print (f" Global efficiency: {eff:.4 f} " )
print (f" Modularity Q: {Q_mod:.4 f} " )
print (f" Communities detected: {len (np.unique(ci))} " )
n = fc_bin.shape[0 ]
m = int (fc_bin.sum () / 2 )
k_avg = degree.mean()
C_rand = k_avg / n
L_rand = np.log(n) / np.log(k_avg)
sigma = (C_nodal.mean() / C_rand) / (L / L_rand)
print (f" Small-world σ: {sigma:.3 f} (>1 = small-world)" )
fig, axes = plt.subplots(1 , 3 , figsize=(14 , 4 ))
metrics_data = [
(degree, "Degree" , "steelblue" ),
(C_nodal, "Clustering coefficient" , "green" ),
(bc, "Betweenness centrality" , "coral" ),
]
for ax, (data, label, color) in zip (axes, metrics_data):
ax.bar(range (n), sorted (data, reverse=True ), color=color, alpha=0.7 )
ax.set_xlabel("ROI rank" ); ax.set_ylabel(label)
ax.set_title(label)
plt.tight_layout()
plt.savefig("brain_network_metrics.png" , dpi=150 )
plt.show()
Step 3: Rich-Club Coefficient and Hub Detection import bct
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def compute_rich_club (fc_bin, n_rand=100 ):
"""
Compute rich-club coefficient and normalize by random networks.
Returns
-------
rc_coeff : ndarray — rich-club coefficients per degree
rc_norm : ndarray — normalized rich-club (> 1 = rich-club present)
"""
rc_coeff = bct.rich_club_bu(fc_bin)
m = int (fc_bin.sum () / 2 )
n = fc_bin.shape[0 ]
rc_rand_all = []
for _ in range (n_rand):
G_rand = nx.gnm_random_graph(n, m, seed=None )
A_rand = nx.to_numpy_array(G_rand, dtype=float )
rc_rand_all.append(bct.rich_club_bu(A_rand))
rc_rand_mean = np.mean(rc_rand_all, axis=0 )
rc_norm = rc_coeff / (rc_rand_mean + 1e-10 )
return rc_coeff, rc_norm
rc, rc_norm = compute_rich_club(fc_bin, n_rand=20 )
degree_z = (degree - degree.mean()) / degree.std()
bc_z = (bc - bc.mean()) / bc.std()
hub_score = (degree_z + bc_z) / 2
hub_threshold = 1.0
hubs = np.where(hub_score > hub_threshold)[0 ]
print (f"\nHub regions (degree + betweenness z > {hub_threshold} ): {len (hubs)} / {n} " )
print (f"Hub indices: {hubs} " )
fig, axes = plt.subplots(1 , 2 , figsize=(12 , 5 ))
degrees = np.arange(1 , len (rc)+1 )
axes[0 ].plot(degrees, rc, 'b-o' , ms=4 , label="Observed RC" )
axes[0 ].plot(degrees, rc_norm, 'r-s' , ms=4 , label="Normalized RC" )
axes[0 ].axhline(1 , color='k' , linestyle='--' , linewidth=0.8 )
axes[0 ].set_xlabel("Degree threshold k" )
axes[0 ].set_ylabel("Rich-club coefficient" )
axes[0 ].set_title("Rich-Club Coefficient" )
axes[0 ].legend()
axes[1 ].scatter(degree_z, bc_z, c=hub_score, cmap="YlOrRd" , s=50 , alpha=0.8 )
axes[1 ].axvline(1 , color='r' , linestyle='--' ); axes[1 ].axhline(1 , color='r' , linestyle='--' )
for h in hubs:
axes[1 ].annotate(f"ROI{h} " , (degree_z[h], bc_z[h]), fontsize=7 )
axes[1 ].set_xlabel("Degree z-score" )
axes[1 ].set_ylabel("Betweenness z-score" )
axes[1 ].set_title("Hub Detection" )
plt.tight_layout()
plt.savefig("rich_club_hubs.png" , dpi=150 )
plt.show()
Advanced Usage
Group Comparison (Patients vs. Controls) import numpy as np
import scipy.stats as stats
from statsmodels.stats.multitest import multipletests
def group_fc_comparison (fc_controls, fc_patients, roi_labels=None , alpha=0.05 ):
"""
Voxel-wise/ROI-wise two-sample t-test on FC matrices.
Parameters
----------
fc_controls : ndarray, (n_controls, n_rois, n_rois)
fc_patients : ndarray, (n_patients, n_rois, n_rois)
Returns
-------
t_matrix, p_matrix, sig_matrix (after FDR correction)
"""
n_rois = fc_controls.shape[1 ]
t_matrix = np.zeros((n_rois, n_rois))
p_matrix = np.ones((n_rois, n_rois))
for i in range (n_rois):
for j in range (i+1 , n_rois):
t, p = stats.ttest_ind(fc_controls[:, i, j], fc_patients[:, i, j])
t_matrix[i, j] = t_matrix[j, i] = t
p_matrix[i, j] = p_matrix[j, i] = p
upper_idx = np.triu_indices_from(p_matrix, k=1 )
p_vals_flat = p_matrix[upper_idx]
_, p_corrected, _, _ = multipletests(p_vals_flat, alpha=alpha, method="fdr_bh" )
sig_matrix = np.zeros_like(p_matrix)
for k, (i, j) in enumerate (zip (*upper_idx)):
sig_matrix[i, j] = sig_matrix[j, i] = p_corrected[k] < alpha
n_sig = int (sig_matrix.sum () / 2 )
print (f"Significant connections (FDR-corrected): {n_sig} " )
return t_matrix, p_matrix, sig_matrix
rng = np.random.default_rng(42 )
n_ctrl, n_pat = 20 , 20
n_rois = 20
fc_ctrl = rng.normal(0 , 0.3 , (n_ctrl, n_rois, n_rois))
fc_pat = rng.normal(0 , 0.3 , (n_pat, n_rois, n_rois))
fc_pat[:, :5 , 5 :10 ] -= 0.3
fc_pat[:, 5 :10 , :5 ] -= 0.3
t_mat, p_mat, sig_mat = group_fc_comparison(fc_ctrl, fc_pat)
Troubleshooting
Error: bct.charpath returns inf Cause : Disconnected graph (path doesn't exist between some nodes).
import networkx as nx
G = nx.from_numpy_array(fc_bin)
Gcc = G.subgraph(max (nx.connected_components(G), key=len ))
fc_connected = nx.to_numpy_array(Gcc)
Issue: Modularity detects only 1 community Cause : Threshold too high (too sparse) or resolution too low.
Q, ci = bct.modularity_und(fc_pos, gamma=1.5 )
Version Compatibility Package Tested versions Known issues bctpy 0.5.0, 0.5.2 Some BCT functions use 1-indexed arrays nilearn 0.10, 0.11 Atlas API stable
External Resources
Official Documentation
Key Papers
Rubinov, M. & Sporns, O. (2010). Complex network measures of brain connectivity: Uses and interpretations . NeuroImage.
Examples
Example 1: Resting-State Networks from Nilearn Atlases
from nilearn import datasets, input_data, connectome
import numpy as np, matplotlib.pyplot as plt
power = datasets.fetch_coords_power_2011()
coords = np.vstack((power.rois["x" ], power.rois["y" ], power.rois["z" ])).T
print (f"Power atlas: {len (coords)} ROIs" )
rng = np.random.default_rng(42 )
n_tp = 300
ts = rng.standard_normal((n_tp, len (coords)))
for net_start in range (0 , len (coords), 20 ):
net_end = min (net_start + 20 , len (coords))
shared = rng.standard_normal(n_tp)
ts[:, net_start:net_end] += 1.5 * shared[:, None ]
fc = np.corrcoef(ts.T)
np.fill_diagonal(fc, 0 )
print (f"FC matrix: {fc.shape} " )
print (f"Mean |FC|: {np.abs (fc[np.triu_indices_from(fc, k=1 )]).mean():.4 f} " )
Interpreting these results : ROIs within the same functional network (default mode, somatomotor, frontoparietal, etc.) show high positive FC. Between-network FC is typically near zero or negative.
Last updated: 2026-03-17 | Maintainer: @xjtulyc
Issues: GitHub Issues
More from this repository
Related occupations SOC
Based on SOC occupation classification