The fmridesign package constructs design matrices for
fMRI general linear model (GLM) analysis. It provides a formula-based
interface for specifying event-related designs with flexible hemodynamic
response functions, parametric modulators, and baseline/nuisance
regressors.
The package separates task-related regressors (event models) from baseline regressors (drift, intercepts, motion), provides informative column names, and includes visualization tools for quality control.
In fMRI analysis, the design matrix (X) is fundamental to the general linear model:
Y = Xβ + ε
Where: - Y: Observed BOLD signal time series - X: Design matrix containing predictors - β: Parameter estimates (regression coefficients) - ε: Error term (noise)
The design matrix typically combines task-related predictors (expected responses to events) and nuisance predictors (drift, motion, physiology). Both are essential for reliable inference.
The package uses two main components:
Event models (event_model()) specify
task-related regressors. You provide onset times, event types, and
optional durations. Each term can use a different HRF basis function.
The model supports factorial designs, parametric modulators, and
trial-specific covariates.
Baseline models (baseline_model())
specify nuisance regressors. You can add polynomial or spline drift
terms, block-wise intercepts, and any additional nuisance regressors
(motion, physiological noise, etc.).
Here’s a minimal example demonstrating the complete workflow from experimental design to design matrix:
# 1. Define the temporal structure (2 runs, 150 scans each, TR = 2s)
TR <- 2
sframe <- sampling_frame(blocklens = c(150, 150), TR = TR)
# 2. Create experimental events
set.seed(123)
# Simple two-condition design with 20 events per run
conditions <- rep(c("A", "B"), each = 10, times = 2)
onsets <- c(
sort(runif(20, 0, 150 * TR - 10)), # Run 1
sort(runif(20, 0, 150 * TR - 10)) # Run 2
)
blockids <- rep(1:2, each = 20)
# 3. Build the event model
emodel <- event_model(
onset ~ hrf(Condition, basis = "spmg1"),
data = data.frame(
onset = onsets,
Condition = factor(conditions),
block = factor(blockids)
),
block = ~ block,
sampling_frame = sframe
)
# 4. Build the baseline model
bmodel <- baseline_model(
basis = "poly",
degree = 3,
sframe = sframe
)
# 5. Extract design matrices
X_task <- design_matrix(emodel)
X_baseline <- design_matrix(bmodel)
# 6. Combine into full design matrix
X_full <- cbind(X_task, X_baseline)
dim(X_full)
#> [1] 300 10
# 7. Visualize the complete design
plot(emodel)The sampling_frame object defines the temporal structure
of your fMRI data:
# Single run: 200 scans, TR = 2 seconds
sframe_single <- sampling_frame(blocklens = 200, TR = 2)
print(sframe_single)
#> Sampling frame
#> - Blocks: 1
#> - Scans: 200 (per block: 200 )
#> - TR: 2 s
#> - Duration: 399 s
# Multiple runs: 3 runs with different lengths
sframe_multi <- sampling_frame(blocklens = c(150, 200, 150), TR = 2)
print(sframe_multi)
#> Sampling frame
#> - Blocks: 3
#> - Scans: 500 (per block: 150, 200, 150 )
#> - TR: 2 s
#> - Duration: 999 sEvents are typically specified with the formula interface:
# Formula interface (recommended)
emodel_formula <- event_model(
onset ~ hrf(condition) + hrf(RT, basis = "gaussian"),
data = data.frame(
onset = c(10, 30, 50, 70),
condition = factor(c("easy", "hard", "easy", "hard")),
RT = c(0.5, 0.8, 0.4, 0.9),
block = factor(c(1, 1, 1, 1))
),
block = ~ block,
sampling_frame = sampling_frame(100, TR = 2)
)# Block design with 20-second blocks
block_onsets <- seq(0, 280, by = 40)
block_conditions <- rep(c("task", "rest"), length.out = length(block_onsets))
block_durations <- rep(20, length(block_onsets))
emodel_block <- event_model(
onset ~ hrf(condition),
data = data.frame(
onset = block_onsets,
condition = factor(block_conditions),
block = factor(rep(1, length(block_onsets)))
),
block = ~ block,
durations = block_durations,
sampling_frame = sampling_frame(150, TR = 2)
)
plot(emodel_block)# Event-related design with RT modulation
set.seed(789)
n_trials <- 30
pm_onsets <- sort(runif(n_trials, 0, 200))
pm_conditions <- rep(c("congruent", "incongruent"), length.out = n_trials)
pm_RT <- rnorm(n_trials, mean = ifelse(pm_conditions == "congruent", 0.5, 0.7), sd = 0.1)
emodel_parametric <- event_model(
onset ~ hrf(condition) + hrf(RT),
data = data.frame(
onset = pm_onsets,
condition = factor(pm_conditions),
RT = scale(pm_RT)[,1], # Center the parametric modulator
block = factor(rep(1, n_trials))
),
block = ~ block,
sampling_frame = sampling_frame(120, TR = 2)
)
# Visualize the parametric modulator
plot(emodel_parametric, term_name = "RT")The design matrices created by fmridesign can be used
with various fMRI analysis approaches:
# Example: Export for use with external software
X <- cbind(design_matrix(emodel), design_matrix(bmodel))
# Save as text file for SPM, FSL, or AFNI
write.table(X, "design_matrix.txt", row.names = FALSE, col.names = TRUE)
# For use with R-based analysis
# Assuming Y is your fMRI time series data
# fit <- lm(Y ~ X - 1) # -1 removes intercept as it's in the design
# Or use with specialized fMRI packages
# library(fmri)
# results <- fmri_glm(Y, X)Inspect your design matrices visually. Check that event timing is correct, HRF shapes are reasonable, and correlations between regressors are acceptable.
Optimize design efficiency. Use jittered inter-stimulus intervals and balanced event types when possible.
Match drift models to run length. Short runs (< 5 minutes) need fewer drift terms than long runs.
Include nuisance regressors. Add motion parameters, physiological noise, and artifact flags to your baseline model.
For more detailed information, see the following vignettes:
vignette("a_03_baseline_model"): In-depth coverage of
baseline and nuisance modelingvignette("a_04_event_models"): Comprehensive guide to
event model specificationIf you encounter issues or have questions:
?event_model,
?baseline_modellist_available_hrfs()