Changelog
Source:NEWS.md
fmriAR 0.3.3
New
fit_noise()can now correct the residual bias in its autocovariance, via a newdesignargument. Autocovariance taken from GLM residuals is biased becauseE[ehat ehat'] = M Sigma Mfor the residual-forming projectionM, which pushesphidown by an amount that grows with the number of regressors: at T = 300 with true AR(1) rho = 0.4, a 9-column design returns about 0.36 and a 28-column design about 0.25. Passingdesign = Xbuilds the linear mapAwithE[gamma_raw] = A gamma_trueand solves it, which removed essentially all of that bias in simulation. The correction is opt-in: it changes estimates, and it needs the design that actually produced the residuals. Currently supported forpooling = "global"and"run"withmethod = "ar"; other combinations raise an error rather than quietly skipping the correction.acvf_bias_matrix()is exported so the matrices can be built once and reused across datasets sharing a design, and fed back throughacvf_correction.correction_max_lag(default 25) sets the lag budget; the correction is exact for noise whose autocovariance dies within it and partial under long memory, so with no high-pass filtering the required budget exceeds 150 and the approach is not practical. Two guards keep it from returning nonsense at the edges. A budget approaching the run length makes the system ill-conditioned – at n = 300 the reciprocal condition number falls from 0.28 at lag 25 to 5e-9 at lag 250, and solving there returnedphi= 0.96 for a truth of 0.5 – so a correction that ill-conditioned is skipped with a warning and that run is left uncorrected. And a design leaving fewer residual degrees of freedom than the lag budget cannot support it, so the budget is reduced to what the data carries, again with a warning. Both failures returned finite, positive, plausible-looking numbers, so neither is caught by checking forNaN.New
noise_acvf()exports the run- and censor-aware autocovariance estimator the package already used internally. Lag products never cross a run boundary or a censoring gap, and the mean is removed per run rather than per fragment. It returns covariances on the scale of the data, along with the pair count behind each lag and the segmentation actually used, so a consumer can tell an estimate backed by thousands of pairs from one backed by three. Previously the only exported route to autocorrelation wasacorr_diagnostics(), which returns normalized ACF for inspection rather than covariance. For a run-stationary process the covariance of any contrast factors assum_h gamma_h * L T_h L', sogammaplus a design gives exact contrast variances without refitting.fmriAR_planobjects now carry the noise scale as well as its shape:gamma(autocovariance, lags 0..p) andsigma2(innovation variance) per pooling unit, plusgamma_by_parcelandsigma2_by_parcelforpooling = "parcel". Previously a plan recorded only the correlation structure, so two datasets differing 100-fold in variance produced identical plans and the magnitude of the noise was unrecoverable without refitting.gammais reported at voxel scale for every pooling mode, andsigma2is derived asgamma_0 - sum_k phi_k gamma_kfrom the coefficients stored on the plan, so the two are always mutually consistent. Under global pooling, runs that reached different lags are truncated to the shortest before averaging rather than zero-padded: a zero-padded autocovariance is not a valid covariance, and buildingSigmafrom one could yield negative contrast variances. Truncation can leavegammashorter thanlength(phi)when censoring is heavy, andsigma2is thenNArather than a partial sum, which would overstate the innovation variance. The condition does not arise below roughly 25% censoring. The addition is purely additive; existing fields are unchanged. Formethod = "arma",gammais the voxel-scale noise autocovariance pooled the same way as for AR, andsigma2isNA: Hannan-Rissanen’s own innovation variance is that of the run-mean series, smaller than the per-voxel value by roughly the number of voxels averaged, so reporting it would understate the noise by that factor.
Fixes
This release fixes several defects that silently produced wrong results rather than errors. Analyses run with pooling = "parcel" or with censor under 0.3.2 should be rerun.
Global pooling now weights each run by the number of uncensored observations that actually contributed. Previously
fit_noise()weighted by the original run length, so a nearly empty run could carry the same influence as a complete run and disagree withnoise_acvf().Run labels are validated and encoded consistently across fitting, ACVF estimation, bias correction, whitening,
acorr_diagnostics(), andafni_restricted_plan(). Wrong-length vectors, missing labels, and labels reused in non-contiguous blocks now fail at the boundary instead of recycling or silently dropping timepoints; contiguous character labels are supported. Previouslyacorr_diagnostics()coerced character labels toNAand silently ignored the run split, andafni_restricted_plan()ordered its run starts by sorted label rather than by time, so runs labelled out of time order produced descending starts.Under global pooling, a run censored down to one or zero surviving frames no longer erases the pooled autocovariance. Such a run carries no
gammaof its own; it previously set the common truncation length to zero, so the plan reportedgammaof length 0 andsigma2 = NAfor the whole fit even thoughphiwas pooled correctly andnoise_acvf()returned the full answer.fit_noise()rejects residuals containingNA,NaN, orInf, matchingnoise_acvf(). It previously returned an order-0 plan with empty coefficients, whichwhiten_apply()accepted and applied as a no-op.Lag budgets are validated the same way in
fit_noise()andnoise_acvf(): both reject a non-finite or non-positivecorrection_max_lag(fit_noise()used to clamp silently to 1), and budgets past the series length are clamped to it rather than overflowing integer coercion into an unrelated error.noise_acvf()underpooling = "global"keys its single pooled unit as"1"even when only one run survives censoring; it previously leaked the surviving run’s label into the name.noise_acvf()now has a separatecorrection_max_lagargument (default 25), matchingfit_noise(), so requesting a short output ACVF no longer weakens residual-bias correction. Itscorrectedfield now reports whether correction was actually applied rather than whether a design was merely supplied.Parcel labels containing
NAor fractional numeric values now fail at the boundary instead of dropping voxels or colliding after integer coercion.Parcel labels that do not survive
as.integer()are now refused at the boundary with a message naming the offending values. Character labels becameNA, matched no voxel, and surfaced far downstream asinvalid K, which names nothing the caller passed. The check covers every exported entry point that accepts parcels:fit_noise()(includingparcel_sets),whiten_apply(),afni_restricted_plan(), andcompat$plan_from_phi(). Integer, numeric, and factor labels are unaffected. Character labels are refused rather than mapped to codes the wayrunsare, becausefit_noise()andwhiten_apply()would each have to derive the same mapping from their own copy of the vector and nothing on the plan records it; convert once withas.integer(factor(parcels))and reuse that coding.Fixed order selection for
pooling = "parcel". The innovation sequence used to score BIC was built with a feedback filter rather than the intended FIR filter, which inflated the variance at every order and made order 0 always win. The effect was that parcel pooling returned all-zero AR coefficients:whiten_apply()returned its inputs unchanged while the plan reported a non-zero order. Order selection now scores the Levinson-Durbin prediction error, matching the global and run paths.Fixed AR estimation under censoring. Each scrubbing fragment was centred on its own mean, which removes the autocorrelation being measured – a two-frame fragment yields a lag-1 correlation of exactly -1 regardless of the data. The estimate was attenuated toward zero as censoring increased (a true phi of 0.6 was recovered as approximately 0 at 40% censoring) and the pooled autocovariance could lose positive semi-definiteness, so
fit_noise()returned non-stationary coefficients andwhiten_apply()then amplified variance instead of whitening. The mean is now estimated per run, while lag products remain confined to contiguous valid segments.enforce_stationary_ar()is now applied on the global and run Yule-Walker paths, which previously returned raw coefficients with no stationarity check.The autocovariance is now positive definite by construction. The unbiased pair-count normalization is retained wherever it is valid, and otherwise the non-zero lags are shrunk toward white noise only as far as needed. Positive definiteness is required with a relative margin rather than mere non-negativity: on the boundary a reflection coefficient is exactly 1, which collapses the Levinson prediction error to its floor, and BIC reads that as a perfect fit and selects the maximum order. The resulting filters amplified variance under censoring instead of whitening.
pooling = "parcel"now honourscensor, which was previously discarded internally, and no longer estimates across run boundaries. Between-run mean offsets previously registered as near-perfect autocorrelation.Fixed
whiten_apply()for parcel plans, which passed the caller’s design matrix to an in-place routine. The caller’sXwas overwritten, everyX_byentry aliased a single matrix, and that matrix had been filtered once per parcel in sequence rather than once with each parcel’s own coefficients.Parcel plans now report the AR order actually fitted instead of the padded length of the coefficient vector.
acorr_diagnostics()now uses itsrunsargument, which was documented and accepted but never referenced, so results were not run-aware.Multiscale pooling sizes the autocovariance to the pooling target rather than the selected order, removing a zero-filling step that drove Yule-Walker to coefficients pinned at the stationarity boundary. The multiscale autocovariance is also estimated with a per-run mean rather than a per-segment one, so the
p_targetandacvf_pooledpaths no longer reintroduce the censoring defect described above.whiten_apply()returns rows in input order for any run labelling. Results were reassembled in sorted run-label order, so runs labelled in any order other than ascending-in-time silently produced a row permutation of the correct answer for bothXandY.Combining
parcel_setswithcensorno longer fails with “incorrect length for ‘group’”.enforce_stationary_ar()now guarantees characteristic roots strictly outside the unit circle. Clamping reflection coefficients alone left the roots on the circle at high order.Order selection is bounded by the available sample size, so BIC can no longer choose AR(8) from eleven observations. An explicitly requested
pis still honoured as given.p_maxat or near the series length no longer fails with “missing value where TRUE/FALSE needed”.method = "arma"now warns when combined withcensor. Censored frames are excluded, but Hannan-Rissanen then runs on the surviving frames spliced together, so its regressions span the gaps and bias both the AR and MA coefficients. Prefermethod = "ar"when censoring is present.whiten_apply()validatesruns: a length mismatch was silently recycled bysplit()and anNAleft that row unwritten in bothXandY. Both now raise an error, and character run labels are accepted.compat$plan_from_phi()works with its documented defaulttheta = NULLfor global and run pooling. It previously reported an MA order of-Infand produced a plan thatwhiten_apply()rejected with “subscript out of bounds” – the plan built by the example in?compat.
fmriAR 0.3.2
CRAN release: 2026-04-15
-
fit_noise()gains acensorparameter for motion scrubbing support. Censored timepoints (e.g., frames with high framewise displacement) are excluded fromAR parameter estimation. Accepts either integer indices or a logical vector. The time series is segmented at censor points and ACVF is pooled across valid segments with proper length-weighting.
whiten()now passescensorto bothfit_noise()andwhiten_apply().The returned
fmriAR_planobject now includes thecensorindices for downstream reference.Fixed a bug where
fit_noise(method = "ar", p = <integer>)could ignore the requested fixed AR order and still perform order selection, sometimes returning a different order.Fixed parcel-mode AR fitting so fixed-order requests only trigger multiscale pooling when that mode is explicitly requested, preserving the expected non-multiscale behavior by default.
Fixed the parcel
pacf_weightedmultiscale path forp_target = 1, where a dimension drop could break coefficient averaging.
fmriAR 0.3.0
- Vignette corrections and clarity improvements.
- Diagnostics: call
acorr_diagnostics()on innovations (whitened residuals). - ARMA section: added note about Hannan-Rissanen estimation on run-mean series.
- Parcel pooling: clarified
X_by[[pid]]dimensions.
fmriAR 0.2.0
CRAN release: 2025-11-03
- Initial CRAN release.
- Core functionality:
fit_noise(),whiten_apply(),whiten(). - AR and ARMA(p,q) noise model estimation.
- Run-aware and parcel-aware pooling options.
- Multi-scale parcel pooling with PACF-weighted and ACVF-pooled modes
- AFNI-compatible restricted AR estimation via
afni_restricted_plan(). - Autocorrelation diagnostics via
acorr_diagnostics(). - Robust standard errors via
sandwich_from_whitened_resid().