Estimates single-trial response amplitudes with the GLMsingle procedure (Prince et al., 2022): a library-based HRF per voxel, GLMdenoise noise regressors chosen by cross-validation, and voxel-wise fractional ridge regression chosen by cross-validation over repeated conditions. The implementation reorganises the computation (per-run factorisation, a single pass of data products per stage, compiled cross-validation and sufficient-statistic R^2) so it is much faster than the reference implementation while computing the same estimator.
Usage
glmsingle(
Y,
design,
tr,
stimdur,
runs = NULL,
hrf_library = NULL,
want_library = TRUE,
want_glmdenoise = TRUE,
want_fracridge = TRUE,
fracs = seq(1, 0.05, by = -0.05),
n_pcs = 10L,
pcstop = 1.05,
xval_scheme = NULL,
session_indicator = NULL,
extra_regressors = NULL,
max_poly_deg = NULL,
brain_thresh = c(99, 0.1),
brain_r2 = NULL,
brain_exclude = NULL,
pc_r2_cutoff = NULL,
pc_r2_cutoff_mask = NULL,
want_percent_bold = TRUE,
want_autoscale = TRUE,
extras_in_denoise = c("always", "with_pcs"),
zero_sd_cv = c("zero", "python"),
singular = c("error", "pinv"),
frac_alpha = c("fracridge", "exact"),
full_glmbadness = FALSE,
chunk_size = 50000L,
n_threads = 1L,
verbose = TRUE
)Arguments
- Y
Data: a list of time x voxel matrices (one per run), or one time x voxel matrix with
runsgiving the run of each row.- design
Either a list of time x condition 0/1 matrices, one per run (GLMsingle's format; a 1 marks a trial onset), or a data frame with columns
run,onset(seconds; must be on the TR grid) andcondition. Repeated conditions drive the cross-validation. For matrixY, event run IDs are matched torunsin data order. For listY, ascending event run IDs correspond to the list order.- tr
Repetition time in seconds.
- stimdur
Trial duration in seconds.
- runs
Run identifier per row of
YwhenYis a single matrix.- hrf_library
Optional time x HRF matrix sampled at the TR (first row at onset). Default:
glmsingle_hrf_library().- want_library
Fit the HRF library per voxel (otherwise the canonical HRF is used for every voxel).
- want_glmdenoise
Derive and select GLMdenoise noise regressors.
- want_fracridge
Fit the fractional ridge model (type D).
- fracs
Ridge fractions in (0, 1] to evaluate. A single value skips cross-validation and uses that fraction.
- n_pcs
Maximum number of noise PCs to evaluate. Capped, with a warning, at the available noise-pool rank across runs. Empty or rank-zero pools use zero PCs and skip PC-count cross-validation.
- pcstop
Stopping factor for choosing the number of PCs. A value
<= 0uses-pcstopPCs without cross-validation.- xval_scheme
List of integer vectors of runs held out together in each cross-validation fold. Default: leave one run out.
- session_indicator
Integer session per run, used to z-score betas within session for cross-validation. Default: one session.
- extra_regressors
Optional list (one per run) of time x regressor nuisance matrices (e.g. motion), or
NULL.- max_poly_deg
Polynomial drift degree, scalar or one per run. Default:
round(run_seconds / 120)as in GLMsingle.- brain_thresh
Two numbers: a percentile of the mean volume and a fraction of it; voxels brighter than their product may enter the noise pool.
- brain_r2
ON-OFF R^2 (percent) below which bright voxels enter the noise pool. Default: estimated tail threshold. With fewer than two distinct finite ON-OFF R^2 values, uses the common value (or zero if none are finite). Thresholds are only estimated when denoising is used.
- brain_exclude
Optional logical vector of voxels to exclude from the noise pool (
FALSEexcludes).- pc_r2_cutoff
ON-OFF R^2 above which voxels summarise the PC-count cross-validation. Default: estimated tail threshold.
- pc_r2_cutoff_mask
Optional logical vector restricting those voxels.
- want_percent_bold
Express betas as percent signal change of the mean volume.
- want_autoscale
Rescale ridge betas to best match the unregularised betas (type D).
- extras_in_denoise
How user extra regressors enter the GLMdenoise and ridge fits:
"always"(default, recommended) or"with_pcs"(only when at least one PC is used; GLMsingle's behaviour).- zero_sd_cv
Treatment of voxels whose reference betas have zero variance within a session during cross-validation:
"zero"(default) or"python"(reproduce GLMsingle's Python behaviour).- singular
What to do when a run's trial regressors are linearly dependent:
"error"(default, as GLMsingle) or"pinv"(minimum-norm solution with a warning).- frac_alpha
How fractions map to ridge penalties:
"fracridge"(default; GLMsingle's grid interpolation) or"exact"(solve the fraction equation exactly; experimental).- full_glmbadness
Compute the PC-count cross-validation for every voxel instead of only the voxels that decide the PC count. Estimates are identical; only the
glmbadnessdiagnostic is filled for all voxels.- chunk_size
Number of voxels processed at a time (GLMsingle's
chunklen). Lower it to reduce peak memory.- n_threads
Threads for the per-voxel C++ loops (
0= OpenMP default). Results do not depend on the thread count. Matrix products use the BLAS library's own threads; usen_threads > 1only with a single-threaded BLAS, because the two thread pools compete for cores.- verbose
Print progress messages.
Value
An object of class glmsingle_fit: a list with elements typea,
typeb, typec, typed (each a list using GLMsingle's field names;
betas are trials x voxels), meanvol, hrf_library, design
(trial bookkeeping), settings and timing.
Details
Four models are returned, matching GLMsingle:
- typea
ON-OFF model: one canonical-HRF regressor for all trials.
- typeb
Single-trial OLS with the best library HRF per voxel.
- typec
Type B plus GLMdenoise noise regressors.
- typed
Type C plus voxel-wise fractional ridge regression.
Argument names
Arguments use snake_case; the GLMsingle names are
wantlibrary (want_library), wantglmdenoise (want_glmdenoise),
wantfracridge (want_fracridge), xvalscheme (xval_scheme),
sessionindicator (session_indicator), maxpolydeg (max_poly_deg),
brainthresh (brain_thresh), brainR2 (brain_r2), brainexclude
(brain_exclude), pcR2cutoff (pc_r2_cutoff), pcR2cutoffmask
(pc_r2_cutoff_mask), wantpercentbold (want_percent_bold),
wantautoscale (want_autoscale) and chunklen (chunk_size). Output
fields keep GLMsingle's names.
Differences from GLMsingle
Defaults reproduce GLMsingle (Python, commit 1ab54a6) except where GLMsingle is internally inconsistent:
extras_in_denoise = "always"keeps user extra regressors in every GLMdenoise and ridge fit; GLMsingle (Python) drops them when zero noise PCs are used, including from the cross-validation reference."with_pcs"reproduces GLMsingle.zero_sd_cv = "zero"gives voxels with zero beta variance no weight in cross-validation; GLMsingle's in-place division helper treats them inconsistently across candidates."python"reproduces it.The ON-OFF R^2 threshold uses a deterministic two-Gaussian mixture fit with a small variance floor (as in GLMsingle's MATLAB code); GLMsingle's Python fit is unseeded.
want_percent_boldapplies to all model types (GLMsingle always scales types C and D).Computation is in double precision (GLMsingle uses single).
HRF indices are 1-based; betas are trials x voxels.
The FIR diagnostic model, hrfmodel = "optimize", wantlss, bootstrap
modes, figures and file outputs are not implemented.
References
Prince, J. S., Charest, I., Kurzawski, J. W., Pyles, J. A., Tarr, M. J., & Kay, K. N. (2022). Improving the accuracy of single-trial fMRI response estimates using GLMsingle. eLife, 11, e77599.
Examples
set.seed(1)
tr <- 1; n_time <- 120; n_vox <- 40
design <- lapply(1:3, function(r) {
D <- matrix(0, n_time, 6)
onsets <- seq(5, 95, by = 8)
D[cbind(onsets, rep_len(sample(6), length(onsets)))] <- 1
D
})
Y <- lapply(design, function(D) {
X <- apply(D, 2, function(s) stats::filter(s, glmsingle_hrf(3, tr),
sides = 1, circular = FALSE))
X[is.na(X)] <- 0
100 + X %*% matrix(rnorm(6 * n_vox, 2), 6) + matrix(rnorm(n_time * n_vox), n_time)
})
fit <- glmsingle(Y, design, tr = tr, stimdur = 3, n_pcs = 2, verbose = FALSE)
dim(fit$typed$betasmd)
#> [1] 36 40