Voxel-wise group-level statistical analysis for neuroimaging data.
GrouVox performs two-sample t-tests and voxel-wise regression on NIfTI brain images with optional covariates (age, sex, etc.), outputs T-statistic maps, and applies multiple comparison correction using Gaussian Random Field (GRF) theory or False Discovery Rate (FDR).
# From source
git clone https://github.com/lijiaxiang63/GrouVox
cd GrouVox
uv syncimport grouvox
# Two-sample t-test
result = grouvox.two_sample_ttest(
group1="data/patients/",
group2="data/controls/",
output="results/group_comparison",
mask="brain_mask.nii.gz",
covariates="covariates.csv", # CSV with age, sex, etc.
contrast=[1, -1], # patients > controls
)
print(f"DOF: {result.dof}, FWHM: {result.fwhm}")
# Voxel-wise regression: image ~ predictor + covariates
reg_result = grouvox.regression(
images="data/subjects/",
predictor="predictor.csv", # single-column CSV, one value per subject
output="results/regression",
mask="brain_mask.nii.gz",
covariates="covariates.csv", # optional nuisance regressors
)
# GRF cluster-level correction
grf_result = grouvox.grf_correction(
"results/group_comparison_T.nii.gz",
mask_path="brain_mask.nii.gz",
voxel_p=0.001,
cluster_p=0.05,
reestimate=False, # True to re-estimate smoothness from the Z-map
)
print(f"Cluster size threshold: {grf_result.cluster_size_threshold} voxels")
for c in grf_result.cluster_table:
mni = c["peak_coords_mni"]
print(f" Cluster {c['label']}: {c['size']} voxels, peak Z={c['peak_value']:.2f} at MNI ({mni[0]:.0f}, {mni[1]:.0f}, {mni[2]:.0f})")
for atlas, regions in c["atlas_regions"].items():
for r in regions:
print(f" {atlas}: {r['pct']:.1f}% {r['name']}")
# FDR correction
fdr_result = grouvox.fdr_correction(
"results/group_comparison_T.nii.gz",
mask_path="brain_mask.nii.gz",
q=0.05,
)
print(f"FDR p-threshold: {fdr_result.p_threshold:.6f}")
print(f"Significant voxels: {fdr_result.n_significant}")
print(f"Clusters: {fdr_result.n_clusters}")
for c in fdr_result.cluster_table:
mni = c["peak_coords_mni"]
print(f" Cluster {c['label']}: {c['size']} voxels at MNI ({mni[0]:.0f}, {mni[1]:.0f}, {mni[2]:.0f})")# Two-sample t-test
grouvox ttest2 \
--group1 data/patients/ \
--group2 data/controls/ \
--output results/group_comparison \
--mask brain_mask.nii.gz \
--covariates covariates.csv \
--contrast 1 -1
# Voxel-wise regression: image ~ predictor + covariates
grouvox regress \
--images data/subjects/ \
--predictor predictor.csv \
--covariates covariates.csv \
--mask brain_mask.nii.gz \
--output results/regression
# GRF correction
grouvox correct \
--input results/group_comparison_T.nii.gz \
--method grf \
--voxel-p 0.001 \
--cluster-p 0.05 \
--mask brain_mask.nii.gz
# GRF correction with smoothness re-estimation from the Z-map
grouvox correct \
--input results/group_comparison_T.nii.gz \
--method grf \
--voxel-p 0.001 \
--cluster-p 0.05 \
--mask brain_mask.nii.gz \
--reestimate
# FDR correction
grouvox correct \
--input results/group_comparison_T.nii.gz \
--method fdr \
--q 0.05 \
--mask brain_mask.nii.gzA CSV file where each row is a subject (for ttest2: group 1 first, then group 2; for regress: same order as the images) and each column is a covariate:
age,sex
25,0
30,1
28,0
...A single-column CSV giving the continuous variable of interest, one value per subject in the same order as the images:
score
12.4
9.8
14.1
...| File | Description |
|---|---|
{output}_T.nii.gz |
T-statistic map (from contrast, or predictor slope for regression) |
{output}_beta.nii.gz |
Beta coefficients (4D: one volume per regressor) |
{output}_cohen_f2.nii.gz |
Cohen's f² effect size map |
| File | Description |
|---|---|
Z_ClusterThresholded_{name}.nii.gz |
Thresholded Z-map |
ClusterThresholded_{name}.nii.gz |
Thresholded original T-map |
ClusterReport_{name}.csv |
Cluster table with atlas annotations |
| File | Description |
|---|---|
FDR_Thresholded_{name}.nii.gz |
FDR-thresholded T-map |
ClusterReport_{name}.csv |
Cluster table with atlas annotations |
Two-sample t-test uses a cell-means parameterization:
X = [G1, G2, Cov1, Cov2, ...]
G1: indicator for group 1 (1 for group 1, 0 for group 2)G2: indicator for group 2- Contrast
[1, -1]tests group 1 > group 2
Regression uses an intercept + predictor parameterization:
X = [1, predictor, Cov1, Cov2, ...]
- The T-map tests the predictor slope (contrast
[0, 1, 0, ...]), i.e. whether voxel values covary with the predictor after accounting for covariates.
Implements cluster-level inference based on Gaussian Random Field theory (Friston et al., 1994):
- Converts T-map to Z-map
- Estimates smoothness from the Z-map (if
--reestimateis set or header metadata is missing), otherwise reads smoothness (dLh) from the NIfTI header - Applies voxel-level threshold
- Calculates cluster size threshold from expected Euler characteristic (cluster_p is halved per tail in two-tailed mode)
- Removes clusters below threshold (26-connectivity)
Implements the Benjamini-Hochberg procedure to control the false discovery rate. Surviving voxels are grouped into clusters via 26-connectivity labeling.
Both GRF and FDR results annotate each surviving cluster using three bundled atlases:
- AAL (Automated Anatomical Labeling)
- HarvardOxford-cortical
- HarvardOxford-subcortical
Each cluster reports:
- Peak voxel MNI coordinates and atlas label at the peak
- Region overlap percentages (proportion of cluster voxels in each atlas region)
- Results are written to a
ClusterReportCSV file and printed by the CLI
uv sync
uv run pytest tests/ -v- Friston, K. J., et al. (1994). Assessing the significance of focal activations using their spatial extent. Human Brain Mapping, 1(3), 210-220.
- Benjamini, Y., & Hochberg, Y. (1995). Controlling the false discovery rate. JRSS-B, 57(1), 289-300.
- Worsley, K. J., et al. (1999). Detecting changes in nonisotropic images. Human Brain Mapping, 8(2-3), 98-101.