General linear model in SPM¶
You should land on this page after having collected your fMRI data, converted it to BIDS and preprocessed it. Your goal now is to model the BOLD activity with a Generalised Linear Model (GLM), in order to obtain the beta values on which to apply further analyses.
You should start here if you are not yet familiar with SPM and what it does. Before scripting your analysis pipeline, take your time to understand the different steps taken by SPM and what they do. Once you're confident with it, move ahead and write your analysis in code (see First-level analysis - scripting).
In this section, we will use the Statistical Parametric Mapping (SPM) package to construct the GLM. Here’s an overview of the steps:
- Data Preparation: Get your files ready for SPM.
- Design Matrix Setup: Define the model for your analysis.
- Model Estimation & Results: Estimate your model and analyze contrasts.
This page will accompany you through the steps and can be used as a guide to the input and output of each function of SPM. It is not a complete tutorial on SPM, however, and there are more extensive resources out there. It can be a good idea to cover the tutorials from Andy Jahn, for instance, as they go in depth into SPM and how it works.
Step 1: Data Preparation¶
Before running the GLM, we need to make sure the data is compatible with SPM. There are two steps that need to be taken to bring your .nii files from fMRIPrep output to SPM input:
- gunzipping (de-compressing
.nii.gzfiles, which SPM can't handle natively) - smoothing (mostly for localizer runs).
The suggested way of proceeding is to create a derivatives/pre-SPM folder where to store a gunzipped output folder and a smoothed output folder.
Decompressing NIfTI Files¶
SPM cannot process .nii.gz files directly, so we first need to decompress them:
- Create a directory for pre-processed files:
- Decompress the files using
gunzipin the terminal:
- Store the decompressed files in a subdirectory called
gunzippedinsidederivatives/pre-SPM.
Decompress with a right-click!
Most Operating Systems will let you decompress the nii.gz files directly from the File Explorer. Right click on the files you want to decompress, and extract them like you would do with a compressed folder.
Smoothing functional data¶
Smoothing is required to increase signal-to-noise ratio, especially for localizer runs. Follow these steps:
- Launch the SPM GUI with the command:
- In the GUI, click on
Smooth. - Select the decompressed
.niifiles. - Set the FWHM (Full Width at Half Maximum) to
[4 4 4]or[6 6 6]for moderate smoothing. - Save the smoothed output in
derivatives/pre-SPM/smoothed.
Automated Preprocessing
You can integrate the decompression and smoothing steps into a script to streamline your workflow, avoiding manual steps (see the Analysis Workflow for an example).
Step 2: Design Matrix Setup¶
To run a GLM, you need to create a design matrix that links the timing of experimental conditions to the observed BOLD response. The design matrix is specified in SPM’s Specify 1st level interface. While this can be done manually for simpler designs, for complex designs with many conditions, it’s more efficient to use externally generated onset time and confounds files.
Creating onset files¶
For complex designs, you should create one onset file per run per subject, containing information about event types, onsets, and durations. The eventsBIDS2SPM function can help convert BIDS-formatted event files into the onset files that SPM requires.
- Use the
eventsBIDS2SPMfunction to convert event files. -
Store the onset files in the following structure:
BIDS/sub-xx/func/sub-xx_run-x_eventsspm.mat.
eventsBIDS2SPM
Creating confounds files¶
Head motion and other confound regressors from fMRIPrep need to be formatted to be compatible with SPM. The fMRIprepConfounds2SPM function can convert these files into the format required by SPM.
- Use the
fMRIprepConfounds2SPMfunction to convert confounds from fMRIPrep. - Store the confounds files in the BIDS structure with SPM-compatible names:
BIDS/sub-xx/func/sub-xx_run-x_confoundsspm.mat.
fMRIprepConfounds2SPM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
BIDS-Compliant Naming
Ensure the files are saved with names that include:
sub-xx: subject identifier.run-x: run identifier.confoundsspmoreventsspmfor confounds and timings, respectively.
Using the Functions in a Script¶
Both eventsBIDS2SPM and fMRIprepConfounds2SPM are MATLAB functions that can be integrated into scripts for batch processing in SPM. This allows you to automate the import of timing and confounds data or save them into SPM-compatible files for later use.
You can use these functions within a MATLAB script to load onset times and confounds directly into an SPM batch job. This is useful when you want to process multiple subjects and runs in one go:
This script converts the timing and confounds information, then immediately feeds them into an SPM batch, making it suitable for automated batch jobs over multiple runs or subject.
If you want to save the converted files for later use, you can use the functions to write them into files with BIDS-compliant names. This is helpful if you need to inspect the files or share them with collaborators before running the GLM analysis:
This script allows you to convert the timing and confound data and save them to files with clear, BIDS-compliant names. These files can later be imported into SPM using the GUI or through further scripting.
Automating Batch Processing
By integrating these functions into a script, you can automate the entire process of setting up the design matrix for multiple subjects and runs. This approach is particularly useful for large datasets, allowing you to focus on refining the analysis rather than manual data preparation.
Specifying the 1st-Level Model in SPM¶
Once you have your onset times and confound regressors files ready, you can set up the design matrix:
- Open SPM: Launch the SPM GUI with
spm fmri - Specify 1st-Level: Go to
Specify 1st Levelin the SPM menu. - Set Parameters:
- Units for design: Set to
seconds. - Interscan interval (TR): Use your fMRI acquisition’s TR value.
- Microtime resolution: This should be the number of slices acquired per TR (e.g.,
64for a 64-slice scan).
- Units for design: Set to
- Input Onset Files:
- Use the multiple conditions option to input onset time files (e.g.,
sub-01_run-01_eventsspm.mat).
- Use the multiple conditions option to input onset time files (e.g.,
- Include Confound Regressors:
- Select the confound regressors from the confound files (e.g.,
sub-01_run-01_confoundsspm.mat).
- Select the confound regressors from the confound files (e.g.,
Reviewing the Design Matrix¶
After specifying the design matrix:
- Click
Review: This will open a visualization of the design matrix. - Check for the following:
- Clear separation between conditions.
- Proper alignment of conditions with the expected timing.
- Inclusion of nuisance regressors (e.g., head motion).
What to Look For
- Boxcar Patterns: Ensure that the regressors follow the expected patterns based on your design.
- Orthogonality: Verify that the conditions are not overly correlated, as this can impact the model’s stability.
Saving the Design Matrix
To save the design matrix visualization for documentation:
- Right-click on the matrix plot and select Save as Image.
- Store the image in your documentation folder.
Step 3: Model Estimation & Results¶
With your design matrix ready, you can estimate the model and define contrasts to test your hypotheses. This step will produce the beta values and residuals necessary for further analysis.
Estimating the Model¶
- Review the Design Matrix: Before estimation, ensure the design matrix looks correct by clicking
Review. -
Estimate the Model: Select
Estimatein the GUI and choose theSPM.matfile.- Set
write residualstoyesto save residual images. -
Click
Runto initiate the model estimation. -
This process will generate beta images (one per regressor) and residual variance estimates.
- Set
Why Save Residuals?
Saving residuals can help diagnose issues with model fit by checking for any systematic patterns in the residual images.
Defining and Evaluating Contrasts¶
Contrasts allow you to test specific hypotheses about the brain's response to different conditions, such as comparing activations between different task conditions or testing against a baseline.
-
Define New Contrasts:
- Go to
Results>Define New Contrast. - Enter a name for the contrast (e.g.,
Condition1 > Condition2). - Specify the weights for each condition based on the order of the regressors (e.g.,
[1 -1]).
- Go to
-
Evaluate the Contrasts:
- After defining contrasts, set the desired threshold (e.g.,
p < 0.001) and clickRunto see the results. - Review the statistical maps for significant clusters or activations.
- After defining contrasts, set the desired threshold (e.g.,
Example contrast
For a comparison between two conditions (e.g., Faces vs. Objects), use:
```
[1 -1]
``
This weightsFacespositively andObjectsnegatively, highlighting brain regions more active during theFaces` condition.
Verifying the order of regressors¶
It’s crucial to confirm the order of regressors in the design matrix before specifying contrasts to ensure that your weights align correctly with the conditions. Here’s how to verify this using both the SPM GUI and by inspecting the SPM.mat file directly.
-
Review the Design Matrix:
- Open SPM and select
Review>SPM.mat. - This opens the design matrix in a new window.
- In the design matrix window, each column represents a different regressor (condition or nuisance variable).
- Open SPM and select
-
Interpret the Design Matrix:
- Hover over each column to see the name and description of the regressor.
- Typically, the first set of columns corresponds to your experimental conditions (e.g.,
Faces,Objects), followed by any nuisance regressors (e.g., motion parameters). - Make a note of the order so that you can set your contrast weights accurately.
Use the design matrix visualization
The design matrix visualization provides a graphical representation of each regressor. Patterns in the design matrix (e.g., boxcar shapes for task conditions) can help you verify the expected structure.
-
Load the
SPM.matFile in MATLAB:In the MATLAB command window, navigate to the folder containing your
This loads theSPM.matfile:SPMstructure into your workspace. -
Explore the Regressors:
To see the names of the regressors, type:
This command will display a list of the regressor names in the order they appear in the design matrix. -
Interpret the Output:
The output will look something like this:
Each string corresponds to a regressor:'Sn(1) condition1*bf(1)' 'Sn(1) condition2*bf(1)' 'Sn(1) condition3*bf(1)' 'Sn(1) motion_x' 'Sn(1) motion_y'Sn(1): Refers to session 1 (the run number).condition1*bf(1): Represents a condition convolved with the basis function (e.g.,Faces).motion_x,motion_y, etc.: These are motion parameters as nuisance regressors.
-
Align the Contrast Weights:
Based on this order, you can now set your contrast weights correctly. For instance, if
condition1is the first regressor andcondition2is the second, a contrast comparing them would be:
Checking Regressors in a Script
If you are scripting the process, you can automate this check by including:
This will print the regressor names directly in the MATLAB command window, helping you confirm the correct order before specifying your contrasts.Why checking regressor order matters?
- Ensures Correct Contrast Specification: Mismatched contrasts can lead to incorrect interpretations of your data, as they might test unintended comparisons.
- Simplifies Troubleshooting: If the results look unexpected, double-checking the regressor order is one of the first steps to identify potential issues in the GLM setup.
- Consistency Across Sessions: If you’re analyzing multiple runs or sessions, verifying regressor order ensures consistency across sessions, which is crucial for second-level analyses.
Visualizing and Saving Results¶
- Viewing Results: Use the SPM results viewer to explore significant clusters.
- Save the Statistical Maps:
- Save thresholded activation maps as
.niifiles using theSavebutton in the results window.
- Save thresholded activation maps as
- Generate Figures:
- Use the
RenderorSurfaceoptions to create visual summaries of your findings. - Save these figures for inclusion in reports or presentations.
- Use the
Exporting Images
To include figures in publications or reports:
- Use Export in SPM to save figures as high-resolution images.
- For 3D brain renderings, adjust the orientation and threshold for a clear presentation.
Visualizing Activations on Volume or Surface¶
To overlay activations on a subject's anatomy:
- Click Display -> overlays... in the SPM GUI.
- Select sections for volume plotting or render for surface plotting.
- Choose the subject's anatomical image from
BIDS/derivatives/fmriprep/sub-xx/anat:- For volume plots, select the
.niifile corresponding to the same space as your GLM (usually MNI). - For surface plots, select the pial or inflated brain image.
- For volume plots, select the
Warning
SPM cannot read .nii.gz files directly, so you must decompress them into .nii files. This can be done with any decompression tool by right-clicking on the file in your file explorer. Once decompressed, use the SPM GUI to select the .nii file.
Directory Structure Reference¶
Below are example directory trees showing file organisation before and after running the first-level GLM.
Before GLM (after preprocessing)¶
BIDS/
├── sub-01/
│ └── func/
│ ├── sub-01_task-exp_run-1_events.tsv
│ ├── sub-01_task-exp_run-1_eventsspm.mat ← onset file (from eventsBIDS2SPM)
│ ├── sub-01_task-exp_run-2_events.tsv
│ └── sub-01_task-exp_run-2_eventsspm.mat
└── derivatives/
├── fmriprep/
│ └── sub-01/
│ ├── anat/
│ │ └── sub-01_space-MNI_desc-preproc_T1w.nii.gz
│ └── func/
│ ├── sub-01_task-exp_run-1_space-MNI_desc-preproc_bold.nii.gz
│ ├── sub-01_task-exp_run-1_desc-confounds_timeseries.tsv
│ ├── sub-01_task-exp_run-2_space-MNI_desc-preproc_bold.nii.gz
│ └── sub-01_task-exp_run-2_desc-confounds_timeseries.tsv
└── pre-SPM/
├── gunzipped/
│ └── sub-01/
│ └── func/
│ ├── sub-01_task-exp_run-1_space-MNI_desc-preproc_bold.nii
│ └── sub-01_task-exp_run-2_space-MNI_desc-preproc_bold.nii
└── smoothed/
└── sub-01/
└── func/
├── ssub-01_task-exp_run-1_space-MNI_desc-preproc_bold.nii
└── ssub-01_task-exp_run-2_space-MNI_desc-preproc_bold.nii
After GLM estimation¶
derivatives/
└── SPM/
└── GLM/
└── sub-01/
└── task-exp/
├── SPM.mat ← model specification and results
├── beta_0001.nii ← beta image for regressor 1
├── beta_0002.nii ← beta image for regressor 2
├── ...
├── con_0001.nii ← contrast image (e.g., CondA > CondB)
├── spmT_0001.nii ← t-statistic map for contrast 1
├── mask.nii ← analysis mask
├── ResMS.nii ← residual mean squares
└── RPV.nii ← resels per voxel (for RFT corrections)
Saving the Design Matrix¶
After specifying your model (before or after estimation), you can save the design matrix visualisation for your records:
- In the SPM Graphics window, the design matrix is displayed after specification.
- Right-click on the design matrix figure and select Save as Image (or Print to File).
- Choose a format (PNG or EPS for publication quality) and save to your GLM output directory.
Alternatively, you can save it programmatically:
% After running spm_spm, the design matrix is in SPM.xX.X
load('SPM.mat');
figure; imagesc(SPM.xX.X); colormap('gray');
title('Design Matrix');
ylabel('Scans'); xlabel('Regressors');
saveas(gcf, 'design_matrix.png');
Tip
Always save a copy of the design matrix. It is essential for verifying that conditions and confounds are correctly modelled, and reviewers may request it.
Step 4: Second-Level (Group) Analysis¶
After running first-level GLMs for each subject, the next step is to test for effects at the group level. This is done by feeding the first-level contrast images (e.g., con_0001.nii) into a second-level model.
When to use second-level analysis¶
- To test whether an effect observed in individual subjects is consistent across the group
- To perform group comparisons (e.g., patients vs. controls)
- To report results in publications (most fMRI papers require group-level statistics)
Note
For multivariate analyses (MVPA, RSA), group statistics are typically handled differently — see the MVPA page. Second-level GLM is primarily for univariate analyses.
Setting up a second-level model in SPM¶
The most common second-level designs are:
Tests whether a contrast is significantly different from zero across subjects (e.g., "Is there significant activation for faces > objects across the group?").
GUI procedure:
- Open SPM and click Specify 2nd-level.
- Set the Directory to your group-level output folder (e.g.,
derivatives/SPM/GLM/group/task-exp/). - Select One-sample t-test as the design.
- Under Scans, select the first-level contrast images from all subjects:
- e.g.,
derivatives/SPM/GLM/sub-01/task-exp/con_0001.nii,derivatives/SPM/GLM/sub-02/task-exp/con_0001.nii, etc.
- e.g.,
- Optionally add covariates (e.g., age, performance scores).
- Click Run (the green play button).
Scripted version:
% Second-level one-sample t-test
matlabbatch{1}.spm.stats.factorial_design.dir = {'derivatives/SPM/GLM/group/task-exp/'};
% Collect contrast images from all subjects
subjects = dir('derivatives/SPM/GLM/sub-*/task-exp/con_0001.nii');
scans = cellfun(@(p,f) fullfile(p,f), {subjects.folder}, {subjects.name}, 'UniformOutput', false);
matlabbatch{1}.spm.stats.factorial_design.des.t1.scans = scans';
% Estimate
matlabbatch{2}.spm.stats.fmri_est.spmmat = {'derivatives/SPM/GLM/group/task-exp/SPM.mat'};
matlabbatch{2}.spm.stats.fmri_est.write_residuals = 0;
matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
% Run
spm_jobman('run', matlabbatch);
Defining group-level contrasts¶
After estimating the second-level model:
- Click Results in the SPM GUI.
- Select the group-level
SPM.mat. - Define contrasts:
- For a one-sample t-test, a single contrast
[1]tests for positive activation,[-1]for deactivation.
- For a one-sample t-test, a single contrast
Interpreting and reporting results¶
When viewing results:
- Set the significance threshold (commonly p < 0.001 uncorrected at voxel level, or p < 0.05 FWE-corrected).
- Set a cluster extent threshold (e.g., k = 10 voxels) to filter out small, isolated activations.
- SPM will display:
- A glass brain showing suprathreshold clusters
- A table of results with peak MNI coordinates, cluster sizes, t-values, and p-values
Reporting conventions
When reporting group results in a publication, include:
- The statistical threshold used (voxel-level and cluster-level)
- Whether correction is FWE, FDR, or uncorrected
- Peak MNI coordinates, t/z-values, and cluster extent for each significant cluster
- The number of subjects included in the analysis
Visualising Results with Python¶
While SPM provides built-in visualisation tools, Python offers more flexibility for creating publication-quality figures.
Glass brain and slice plots with nilearn¶
from nilearn import plotting
# Glass brain plot of a statistical map
plotting.plot_glass_brain(
'path/to/spmT_0001.nii',
threshold=3.0,
title='Group activation (t > 3.0)',
display_mode='lyrz'
)
plotting.show()
Surface plots with nilearn¶
Nilearn's plot_surf_stat_map is the recommended way to create surface visualisations. Using the plotly engine gives interactive plots and is significantly faster:
from nilearn import plotting, surface, datasets
# Fetch fsaverage surface mesh
fsaverage = datasets.fetch_surf_fsaverage()
# Plot a statistical map on the inflated surface
plotting.plot_surf_stat_map(
fsaverage.infl_left,
stat_map=stat_map_lh, # surface stat map (e.g., from nilearn GLM)
hemi='left',
view='lateral',
threshold=3.0,
colorbar=True,
cmap='cold_hot',
engine='plotly' # interactive and faster than matplotlib
)
Tip
Set engine='plotly' for interactive rotation and zooming in notebooks. Use engine='matplotlib' when you need a static image for a publication figure.
An alternative for more customised multi-panel surface figures is surfplot, which provides finer control over layout and layering.
FreeSurfer surface geometries¶
fMRIPrep (via FreeSurfer's recon-all) produces several surface reconstructions for each hemisphere:
| Surface | Description |
|---|---|
| pial | The outer cortical surface (grey matter / CSF boundary). |
| white | The inner cortical surface (grey / white matter boundary). |
| inflated | The cortex "inflated" so that sulci are visible — useful for visualisation. |
| sphere | The cortex mapped onto a sphere — used for inter-subject alignment (e.g., fsaverage registration). |
fMRIPrep can output BOLD data projected onto fsaverage or individual surfaces (in GIFTI .gii format), which can then be used for surface-based group analysis. For cross-space transformations (e.g., MNI volume to fsaverage surface), see neuromaps.
For more details: