Skip to content

Commit bc25d11

Browse files
eurunuelaclaude
andcommitted
Update 05_3dMEPFM.md for pySPFM v2.0 API
Migrate notebook from legacy pySPFM.pySPFM() function to new scikit-learn style SparseDeconvolution class API: - Use SparseDeconvolution with fit/transform pattern - Add NiftiMasker for data loading and inverse transformation - Concatenate multi-echo data along time axis as required by new API - Add documentation of model attributes (coef_, lambda_, hrf_matrix_) - Save fitted signal and residuals as additional outputs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent fad51ae commit bc25d11

File tree

1 file changed

+58
-7
lines changed

1 file changed

+58
-7
lines changed

content/05_3dMEPFM.md

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import json
1717
import os
1818
from glob import glob
1919
20-
import nibabel as nb
20+
import nibabel as nib
21+
import numpy as np
22+
from nilearn.maskers import NiftiMasker
2123
2224
data_path = os.path.abspath('../DATA')
2325
```
@@ -53,16 +55,65 @@ out_dir = os.path.join(data_path, "pySPFM")
5355
```{code-cell} ipython3
5456
:tags: [output_scroll]
5557
56-
from pySPFM import pySPFM
58+
from pySPFM import SparseDeconvolution
5759
58-
pySPFM.pySPFM(
59-
data_fn=data_files,
60-
mask_fn=mask_file,
61-
output_filename=os.path.join(out_dir, "out"),
60+
# Create masker to convert 4D NIfTI data to 2D array
61+
masker = NiftiMasker(mask_img=mask_file)
62+
63+
# Load and mask each echo, then concatenate along time axis
64+
# For multi-echo data, timepoints from different echoes are concatenated along the first axis
65+
masked_data = []
66+
for f in data_files:
67+
echo_data = masker.fit_transform(f) # Shape: (n_timepoints, n_voxels)
68+
masked_data.append(echo_data)
69+
70+
X = np.vstack(masked_data) # Shape: (n_timepoints * n_echoes, n_voxels)
71+
72+
# Fit the sparse deconvolution model
73+
model = SparseDeconvolution(
6274
tr=2.47,
63-
out_dir=out_dir,
6475
te=echo_times,
76+
criterion="bic",
6577
)
78+
model.fit(X)
79+
80+
# Get the deconvolved activity-inducing signals
81+
activity = model.coef_ # Shape: (n_timepoints, n_voxels)
82+
83+
# Transform back to NIfTI image and save
84+
os.makedirs(out_dir, exist_ok=True)
85+
activity_img = masker.inverse_transform(activity)
86+
activity_img.to_filename(os.path.join(out_dir, "out_activity.nii.gz"))
87+
88+
# Also save the regularization parameter values
89+
np.save(os.path.join(out_dir, "out_lambda.npy"), model.lambda_)
90+
91+
print(f"Activity shape: {activity.shape}")
92+
print(f"Saved activity to: {os.path.join(out_dir, 'out_activity.nii.gz')}")
93+
```
94+
95+
The `SparseDeconvolution` model provides several useful attributes and methods after fitting:
96+
97+
- `coef_`: The deconvolved activity-inducing signals
98+
- `lambda_`: The regularization parameter values
99+
- `hrf_matrix_`: The HRF convolution matrix used
100+
- `get_fitted_signal()`: Returns the fitted (reconstructed) signal
101+
- `get_residuals(X)`: Returns the residuals between the original data and fitted signal
102+
103+
```{code-cell} ipython3
104+
# Get the fitted signal and residuals
105+
fitted_signal = model.get_fitted_signal()
106+
residuals = model.get_residuals(X)
107+
108+
# Save additional outputs
109+
fitted_img = masker.inverse_transform(fitted_signal)
110+
fitted_img.to_filename(os.path.join(out_dir, "out_fitted.nii.gz"))
111+
112+
residuals_img = masker.inverse_transform(residuals)
113+
residuals_img.to_filename(os.path.join(out_dir, "out_residuals.nii.gz"))
114+
115+
print(f"Fitted signal shape: {fitted_signal.shape}")
116+
print(f"Residuals shape: {residuals.shape}")
66117
```
67118

68119
The pySPFM workflow writes out a number of files.

0 commit comments

Comments
 (0)