Usage#
pySPFM provides scikit-learn compatible estimators for sparse hemodynamic deconvolution of fMRI data.
There are two main approaches: using a fixed regularization parameter \(\lambda\), or using stability
selection for more robust but computationally intensive estimation.
Python API#
The recommended way to use pySPFM is through the Python API with scikit-learn style estimators:
from pySPFM import SparseDeconvolution
import nibabel as nib
from nilearn.maskers import NiftiMasker
# Load and mask your fMRI data
img = nib.load("my_fmri_data.nii.gz")
masker = NiftiMasker(mask_img="my_mask.nii.gz")
X = masker.fit_transform(img) # Shape: (n_timepoints, n_voxels)
# Fit the deconvolution model
model = SparseDeconvolution(tr=2.0, criterion="bic")
model.fit(X)
# Get deconvolved activity-inducing signals
activity = model.coef_ # Shape: (n_timepoints, n_voxels)
Univariate vs Multivariate Mode#
The SparseDeconvolution estimator supports two solving modes controlled by the group parameter.
This is a key design decision that affects how the regularization problem is solved.
Univariate Mode (default)#
When group=0.0 (the default), each voxel is solved independently using pure L1 (LASSO)
regularization. This is the standard approach and is computationally efficient:
# Univariate mode: each voxel solved independently
model = SparseDeconvolution(
tr=2.0,
criterion="bic", # Can use LARS criteria (bic, aic)
group=0.0, # Pure L1/LASSO regularization
n_jobs=4, # Parallelize across voxels
)
model.fit(X)
In univariate mode:
Each voxel has its own regularization parameter \(\lambda\)
Computation can be parallelized across voxels via
n_jobsAll criteria are available:
'bic','aic','mad','factor', etc.Best for exploratory analysis or when spatial structure is not important
Multivariate Mode (Spatial Grouping)#
When group > 0, all voxels are solved jointly using L2,1 mixed-norm regularization.
This encourages spatial grouping of activity—if a neural event occurs at a particular
timepoint, it tends to be detected across neighboring voxels simultaneously
(Uruñuela et al., 2024):
# Multivariate mode: all voxels solved jointly with spatial regularization
model = SparseDeconvolution(
tr=2.0,
criterion="factor", # Must use FISTA criteria (not bic/aic)
group=0.5, # L2,1 + L1 mixed-norm regularization
)
model.fit(X)
The group parameter controls the balance between L1 and L2,1 norms:
Value |
Regularization |
Description |
|---|---|---|
|
Pure L1/LASSO |
Univariate, no spatial structure |
|
L1 + L2,1 mix |
Elastic net-like combination |
|
Pure L2,1 |
Maximum spatial grouping |
In multivariate mode:
A single \(\lambda\) is used for all voxels
Computation cannot be parallelized (joint optimization)
Only FISTA criteria are available:
'mad','factor','ut','lut','pcg','eigval'Leverages the spatial structure of 4D fMRI data
When to use multivariate mode:
When you expect spatially coherent neural activity
When analyzing regions of interest (ROIs) with known spatial structure
When you want to leverage the 4D structure of fMRI data
When reducing false positives through spatial consistency is important
Mathematical Background#
The deconvolution problem is formulated as:
where:
\(\mathbf{y}\) is the BOLD signal
\(\mathbf{H}\) is the HRF convolution matrix
\(\mathbf{s}\) is the activity-inducing signal
\(R(\mathbf{s})\) is the regularization term
Univariate mode (group=0): \(R(\mathbf{s}) = \|\mathbf{s}\|_1\) (L1 norm, LASSO)
Multivariate mode (group > 0):
\(R(\mathbf{s}) = (1 - \text{group}) \|\mathbf{s}\|_1 + \text{group} \|\mathbf{S}\|_{2,1}\)
where \(\|\mathbf{S}\|_{2,1} = \sum_t \|\mathbf{s}_t\|_2\) is the L2,1 mixed norm that encourages rows of the coefficient matrix (timepoints) to be jointly zero or non-zero across voxels.
Lambda Selection Criteria#
For a comprehensive review of regularization parameter selection methods in hemodynamic deconvolution, see Uruñuela et al. (2023).
LARS-based criteria (univariate only)#
The following criteria use the Least Angle Regression (LARS) algorithm to efficiently compute the full regularization path and select \(\lambda\) using information criteria:
'bic': Bayesian Information Criterion - penalizes model complexity more heavily'aic': Akaike Information Criterion - less penalty for model complexity
Note
LARS criteria ('bic', 'aic') are only available in univariate mode (group=0).
They cannot be used with spatial regularization because LARS solves each voxel independently.
FISTA-based criteria (univariate and multivariate)#
The following criteria use the FISTA (Fast Iterative Shrinkage-Thresholding Algorithm) solver and support both univariate and multivariate modes:
'mad': Median Absolute Deviation of fine-scale wavelet coefficients (Daubechies-3). Robust noise estimation (Karahanoğlu et al., 2013; Uruñuela et al., 2023).'mad_update': MAD with iterative updating. The noise estimate is refined during optimization: \(\lambda_{n+1} = \frac{N \sigma}{\frac{1}{2} \|\mathbf{y} - \mathbf{Hs}\|_2^2 \lambda_n}\)'ut': Universal Threshold: \(\lambda = \sigma \sqrt{2 \log(T)}\), where \(T\) is the number of timepoints.'lut': Lower Universal Threshold: \(\lambda = \sigma \sqrt{2 \log(T) - \log(1 + 4\log(T))}\)'factor': Factor of noise estimate: \(\lambda = \text{factor} \times \sigma\), controlled by thefactorparameter.'pcg': Percentage of maximum lambda: \(\lambda = \text{pcg} \times \|\mathbf{H}^T \mathbf{y}\|_\infty\), controlled by thepcgparameter.'eigval': Based on eigenvalue decomposition of the HRF matrix.
Command Line Interface#
pySPFM also provides a command-line interface for common workflows.
Sparse Deconvolution#
pySPFM sparse -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject -d my_results_directory \
-tr 2.0 --criterion mad
With HRF model selection:
# Using the Glover HRF
pySPFM sparse -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject -d my_results_directory \
-tr 2.0 --criterion bic --hrf-model glover
# Using a custom HRF from file
pySPFM sparse -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject -d my_results_directory \
-tr 2.0 --criterion mad --hrf-model /path/to/my_hrf.1D
# Using the block model for sustained activity
pySPFM sparse -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject -d my_results_directory \
-tr 2.0 --criterion factor --block
For multi-echo data:
pySPFM sparse -i echo1.nii.gz echo2.nii.gz echo3.nii.gz \
-m my_mask.nii.gz -te 14.5 38.5 62.5 \
-o my_subject -d my_results_directory \
-tr 2.0 --criterion mad
Stability Selection#
The stability selection procedure provides more robust estimates by solving the regularization problem across multiple subsampled surrogate datasets (Uruñuela et al., 2020):
pySPFM stability -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject_stability -d my_results_directory \
-tr 2.0 --n-surrogates 50
Low-Rank Plus Sparse (SPLORA)#
For decomposing fMRI data into low-rank (structured noise) and sparse (neural activity) components (Uruñuela et al., 2021):
pySPFM lowrank -i my_fmri_data.nii.gz -m my_mask.nii.gz \
-o my_subject_lowrank -d my_results_directory \
-tr 2.0 --criterion factor
AUC to Estimates#
The auc_to_estimates workflow converts stability selection AUC (Area Under the Curve) maps
back into activity-inducing signal estimates. This is useful when you’ve run stability selection
and want to obtain debiased neural activity estimates from the AUC scores.
Python API#
from pySPFM.cli.auc_to_estimates import auc_to_estimates
# Basic usage: convert AUC to activity estimates
auc_to_estimates(
data_fn=["my_fmri_data.nii.gz"],
auc_fn="my_auc.nii.gz",
mask_fn=["my_mask.nii.gz", "my_roi_mask.nii.gz"],
output_filename="my_subject_estimates",
tr=2.0,
thr=95.0, # 95th percentile threshold
out_dir="my_results_directory",
)
# With time-dependent thresholding
auc_to_estimates(
data_fn=["my_fmri_data.nii.gz"],
auc_fn="my_auc.nii.gz",
mask_fn=["my_mask.nii.gz", "my_roi_mask.nii.gz"],
output_filename="my_subject_estimates",
tr=2.0,
thr=90.0,
thr_strategy="time", # Apply threshold at each TR
out_dir="my_results_directory",
)
# Multi-echo data
auc_to_estimates(
data_fn=["echo1.nii.gz", "echo2.nii.gz", "echo3.nii.gz"],
auc_fn="my_auc.nii.gz",
mask_fn=["my_mask.nii.gz"],
output_filename="my_subject_estimates",
tr=2.0,
te=[14.5, 38.5, 62.5], # Echo times in ms
thr=95.0,
out_dir="my_results_directory",
)
# Block model with grouping
auc_to_estimates(
data_fn=["my_fmri_data.nii.gz"],
auc_fn="my_auc.nii.gz",
mask_fn=["my_mask.nii.gz", "my_roi_mask.nii.gz"],
output_filename="my_subject_estimates",
tr=2.0,
thr=95.0,
block_model=True, # Estimate innovation signals
group=True, # Group consecutive coefficients
group_distance=3, # Max distance between grouped coefficients
out_dir="my_results_directory",
)
Command Line Interface#
auc_to_estimates -i my_fmri_data.nii.gz -a my_auc.nii.gz \
-m my_mask.nii.gz my_roi_mask.nii.gz \
-o my_subject_estimates -d my_results_directory \
-tr 2.0 -thr 95
Key parameters:
Parameter |
Description |
|---|---|
|
Input fMRI data file(s) |
|
AUC map from stability selection |
|
Brain mask and optional AUC thresholding mask (mask for the fMRI data and the AUC thresholding mask) |
|
Percentile threshold (1-100) or absolute threshold [0, 1) (i.e., 0 to less than 1). Default: 95 |
|
Thresholding strategy: static or time (time-dependent). Default: static |
|
Estimate innovation signals (block model) |
|
Consider consecutive coefficients as belonging to the same block |
|
Maximum distance between coefficients in the same block. Default: 3 |
CLI example with time-dependent thresholding:
auc_to_estimates -i my_fmri_data.nii.gz -a my_auc.nii.gz \
-m my_mask.nii.gz my_roi_mask.nii.gz \
-o my_subject_estimates -d my_results_directory \
-tr 2.0 -thr 90 --strategy time
CLI example with multi-echo data:
auc_to_estimates -i echo1.nii.gz echo2.nii.gz echo3.nii.gz \
-a my_auc.nii.gz -m my_mask.nii.gz \
-o my_subject_estimates -d my_results_directory \
-tr 2.0 -te 14.5 38.5 62.5 -thr 95
HRF Model Configuration#
The hemodynamic response function (HRF) is central to deconvolution. pySPFM supports
three ways to specify the HRF model via the hrf_model parameter:
Built-in HRF Models#
pySPFM provides two canonical HRF models from the literature:
from pySPFM import SparseDeconvolution
# SPM canonical HRF (default)
model_spm = SparseDeconvolution(tr=2.0, hrf_model="spm")
# Glover HRF
model_glover = SparseDeconvolution(tr=2.0, hrf_model="glover")
Model |
Description |
|---|---|
|
SPM canonical HRF with default parameters (default) |
|
Glover HRF model from FSL/FreeSurfer |
Both models are implemented via nilearn and are commonly used in fMRI analysis. The SPM model is the default as it is widely adopted.
Custom HRF from File#
For advanced users who need a specific HRF shape (e.g., from a separate HRF estimation
procedure), you can provide a custom HRF as a .1D or .txt file:
from pySPFM import SparseDeconvolution
# Use a custom HRF from a text file
model = SparseDeconvolution(
tr=2.0,
hrf_model="/path/to/my_custom_hrf.1D",
)
Custom HRF file requirements:
Format: Plain text file with one value per line (
.1Dor.txtextension)Length: Must not exceed the number of scans in your data
Sampling: Should be sampled at the TR of your acquisition
Example custom HRF file (my_hrf.1D):
0.0
0.1
0.5
1.0
0.8
0.4
0.1
0.0
-0.1
-0.05
0.0
Block vs Spike Model#
The block_model parameter controls what type of signal is estimated:
# Spike model (default): estimate activity-inducing signals
model_spike = SparseDeconvolution(
tr=2.0,
block_model=False, # Default
)
# Block model: estimate innovation signals (step functions)
model_block = SparseDeconvolution(
tr=2.0,
block_model=True,
)
Parameter |
Signal Type |
Description |
|---|---|---|
|
Activity-inducing |
Neural events as impulses (spikes) |
|
Innovation |
Sustained activity as step functions (blocks) |
When block_model=True, the HRF matrix is modified to include an integrator
(cumulative sum), which models sustained activity that starts at one timepoint
and continues. This is useful for paradigm-free mapping of block-like neural responses.
Accessing the HRF Matrix#
After fitting, you can inspect the HRF convolution matrix:
model = SparseDeconvolution(tr=2.0, hrf_model="glover")
model.fit(X)
# The HRF matrix used for deconvolution
print(f"HRF matrix shape: {model.hrf_matrix_.shape}")
# Shape: (n_timepoints * n_echoes, n_timepoints)
Examples#
Basic single-echo deconvolution#
from pySPFM import SparseDeconvolution
import numpy as np
# Simulate data: 200 timepoints, 100 voxels
X = np.random.randn(200, 100)
# Univariate deconvolution with BIC
model = SparseDeconvolution(tr=2.0, criterion="bic")
model.fit(X)
print(f"Coefficients shape: {model.coef_.shape}")
print(f"Lambda values shape: {model.lambda_.shape}")
Multi-echo deconvolution#
from pySPFM import SparseDeconvolution
import numpy as np
# Multi-echo data: stack echoes along time axis
# 3 echoes × 200 timepoints = 600 rows
X = np.random.randn(600, 100)
model = SparseDeconvolution(
tr=2.0,
te=[14.5, 38.5, 62.5], # Echo times in ms
criterion="mad",
)
model.fit(X)
Using different HRF models#
from pySPFM import SparseDeconvolution
import numpy as np
X = np.random.randn(200, 100)
# Using the Glover HRF instead of SPM
model_glover = SparseDeconvolution(
tr=2.0,
hrf_model="glover",
criterion="bic",
)
model_glover.fit(X)
# Using a custom HRF from file
model_custom = SparseDeconvolution(
tr=2.0,
hrf_model="/path/to/my_estimated_hrf.1D",
criterion="mad",
)
# model_custom.fit(X) # Uncomment with valid path
Block model for sustained activity#
from pySPFM import SparseDeconvolution
import numpy as np
X = np.random.randn(200, 100)
# Use block model for paradigm-free mapping of sustained activity
model = SparseDeconvolution(
tr=2.0,
block_model=True, # Estimate innovation signals (step functions)
criterion="factor",
factor=1.0,
)
model.fit(X)
# coef_ now contains innovation signals representing
# onsets/offsets of sustained activity
Multivariate deconvolution with spatial grouping#
from pySPFM import SparseDeconvolution
import numpy as np
X = np.random.randn(200, 100)
# Use spatial grouping to encourage consistent activity across voxels
model = SparseDeconvolution(
tr=2.0,
criterion="factor", # FISTA criterion required for group > 0
group=0.5, # 50% L2,1 + 50% L1
factor=1.0,
)
model.fit(X)
# In multivariate mode, a single lambda is used for all voxels
print(f"Lambda (same for all voxels): {model.lambda_[0]}")
Stability selection for robust estimation#
from pySPFM import StabilitySelection
import numpy as np
X = np.random.randn(200, 100)
model = StabilitySelection(
tr=2.0,
n_surrogates=50, # Number of subsampled datasets
subsample_fraction=0.5,
)
model.fit(X)
# Get the stability scores (AUC)
auc = model.auc_
References#
Core Methodology#
Uruñuela et al. (2023): Hemodynamic Deconvolution Demystified: Sparsity-Driven Regularization at Work. Aperture Neuro. Comprehensive review of sparse deconvolution methods and regularization strategies.
Caballero-Gaudes et al. (2013): Paradigm free mapping with sparse regression. Human Brain Mapping.
Karahanoğlu et al. (2013): Total activation: fMRI deconvolution through spatio-temporal regularization. NeuroImage.
Stability Selection#
Uruñuela et al. (2020): Stability-based sparse paradigm free mapping algorithm for deconvolution of functional MRI data. IEEE EMBC. Introduces the stability selection approach for robust sparse deconvolution.
Multivariate / Whole-Brain Deconvolution#
Uruñuela et al. (2024): Whole-brain multivariate hemodynamic deconvolution for functional MRI with stability selection. Medical Image Analysis. Extends sparse deconvolution to whole-brain multivariate analysis.
Uruñuela et al. (2019): Deconvolution of multi-echo functional MRI data with multivariate multi-echo sparse paradigm free mapping. ISMRM.
Low-Rank Plus Sparse (SPLORA)#
Uruñuela et al. (2021): A low rank and sparse paradigm free mapping algorithm for deconvolution of fMRI data. IEEE ISBI. Introduces the SPLORA algorithm for separating global and neural components.