GLMdenoise: a fast, automated technique for denoising task-based fMRI data
Kendrick Kay, Ariel Rokem, Jon Winawer, Bob Dougherty, Brian Wandell
Questions, comments? E-mail Kendrick |
News
- 2014/08/01 - Release of version 1.4.
- 2014/07/14 - Release of version 1.31.
- 2013/12/13 - Release of version 1.3.
- 2013/12/12 - Three new example scripts added.
- 2013/12/06 - GLMdenoise paper published.
- 2013/11/18 - Release of version 1.2.
- 2013/05/12 - Release of version 1.1. Many new useful features.
- 2013/05/10 - A new FAQ with many useful bits of information (see below).
History of major code changes
- 2014/08/01 - Version 1.4. Add option for obtaining parametric GLM fits and error estimates. Add new DVARS sanity-check figures. Change polynomial functions such that they are all mutually orthogonal and unit-length (this may cause slight changes to behavior due to numerical precision issues).
- 2014/07/14 - Version 1.31. Minor fixes to improve platform compatibility.
- 2013/12/13 - Version 1.3. Added new example scripts.
- 2013/11/18 - Version 1.2. The default canonical HRF has changed. (It is now smoother/nicer than before.) If pcvoxels is determined to be empty (e.g. no voxels were above 0% for any number of PCs), we now issue a warning (instead of crashing) and fallback to simply using the top 100 voxels to select the number of PCs. Use fullfile to ensure compatibility with different platforms.
- 2013/05/12 - Version 1.1. The experimental design can now be specified in terms of onset times.
This means that conditions do not have to exactly coincide with the TRs. Fixed a bug concerning extraregressors. Additional optional inputs, additional outputs, and additional figures. All-zero regressors (e.g. encountered via bootstrapping) are now assigned zero weight.
- 2012/12/03 - Version 1.02. Various speed-ups and minor bug fixes.
- 2012/11/24 - Version 1.0.
Introduction
GLMdenoise is a technique for denoising task-based fMRI data. The basic idea is to derive noise regressors from voxels unrelated to the experimental paradigm and to use these regressors in a general linear model (GLM) analysis of the data. The technique does not require external physiological monitoring nor does it require extra fMRI data—thus, it can be retrospectively applied to existing datasets. The technique is described in the following paper:
Kay, K.N., Rokem, A., Winawer, J., Dougherty, R.F., & Wandell, B.A. GLMdenoise: a fast, automated technique for denoising task-based fMRI data. Frontiers in Neuroscience (2013).
The technique is fast and automated and general: it requires only a design matrix indicating the experimental design and an fMRI dataset. The technique does make a few assumptions, however. First, it assumes that voxels unrelated to the experimental paradigm are contained in the dataset (e.g. voxels in other brain areas, white matter, etc.). Second, the technique assumes that there are at least two runs available and that experimental conditions are repeated across runs. This requirement is necessary because GLMdenoise involves cross-validation across runs.
The amount of improvement in signal-to-noise ratio (SNR) provided by GLMdenoise depends on a variety of factors, such as the intrinsic SNR level, the amount of data collected, and the experimental design. We suspect that SNR gains will tend to be more substantial for low-SNR regimes such as event-related experiments where stimulus durations are brief and evoked fMRI responses are small.
We provide MATLAB code implementing GLMdenoise. The code consists of standard (uncompiled) MATLAB functions, takes advantage of MATLAB's built-in multithreading, and requires only the Statistics Toolbox. The code has been tested on MATLAB 7.6 and 7.9, and should probably work with any recent version of MATLAB.
If you use GLMdenoise in your research, please cite the above paper.
Getting started
Download code (latest tagged version 1.4)
Download code (latest development version)
Clone code from github
After downloading and unzipping the files, launch MATLAB and change to the directory containing the files. You can then run the example scripts by typing "example1", "example2", etc. More information on the example scripts is provided below.
Download example dataset (82 MB)
This dataset is required to run the example scripts. Place the dataset inside the code directory. Alternatively, the example scripts will automatically download the dataset.
Join the GLMdenoise discussion group
This mailing list is for anyone interested in using the GLMdenoise toolbox. Announcements regarding GLMdenoise will be made to this mailing list.
Browse code in HTML (version 1.4)
Basics of the code
The input and output format of the code is intentionally generic. We would be happy to help integrate the code with existing analysis packages.
Inputs. The code requires the following inputs:
- 'design' - This is a design matrix that indicates the experimental design. The dimensions are time x conditions. Each column corresponds to one condition and should consist of 0s except for 1s indicating the onset of the condition. To indicate design matrices for multiple runs, construct a cell vector like { design1 design2 design3 ... }.
Alternatively, the experimental design can be specified in terms of onset times. This allows cases where conditions do not exactly coincide with the TRs of the data. The format is {{C1_1 C2_1 C3_1 ...} {C1_2 C2_2 C3_2 ...} ...} where Ca_b is a vector of onset times (in seconds) for condition a in run b. Time starts at 0 and is coincident with the acquisition of the first volume. Note that if you use the onset-time specification of the experimental design, you will be forced to assume a fixed HRF.
- 'data' - This is the fMRI time-series data. The dimensions are X x Y x Z x time (which indicates that the data has 3D structure); alternatively, the dimensions can be voxels x time (but in this case, some figures will not be informative to look at). The data should contain finite values (e.g. no NaNs) and should be in single-format (if not, we automatically convert to single-format). Voxels with invalid data can be set such that their time-series consist of all 0s. To indicate data for multiple runs, construct a cell vector like { data1 data2 data3 ... }.
- 'stimdur' - The duration of a condition in seconds (e.g. 3). This input is used only to generate an initial seed for the HRF.
- 'tr' - The sampling rate (TR) of the data in seconds (e.g. 2).
Note that the fMRI data should already be pre-processed. We recommend that at a minimum, slice time correction and motion correction should be performed and the first few volumes of each run should be discarded to allow magnetization to reach steady-state. We do not recommend detrending or high-pass filtering the time-series; this is because polynomial regressors are included in the GLM and it is more accurate to model low-frequency fluctuations as part of the GLM. Spatial smoothing and/or atlas normalization are fine as pre-processing steps.
Hint: Make sure that you have discarded the first few volumes of each run (to avoid initial magnetization effects) and that your data matrices do not have any NaNs. Also, make sure that your data has not been mean-subtracted (or converted to percent BOLD change). For example, if you view a slice of your data, your data should look like a brain (values should largely be positive).
Code execution. The most basic call to the code is:
[results,denoiseddata] = GLMdenoisedata(design,data,stimdur,tr,[],[],[],'figures');
This command writes diagnostic figures to the directory 'figures' and returns outputs in 'results' and 'denoiseddata'.
Outputs. The code produces the following outputs:
- 'results' - This contains the GLM analysis results, including estimates of the hemodynamic response function (HRF) and estimates of BOLD response amplitudes (beta weights). The 'results' variable is a struct --- it may be useful to use the "-struct" functionality of the MATLAB save command when saving the results to disk.
- 'denoiseddata' - This contains the denoised data, that is, the time-series data with the component of the data that is estimated to be due to the global noise regressors removed. The point of this output is to make it easy for the code to be integrated into existing analysis workflows (i.e., the user can choose to ignore the GLM analysis results and treat the code as a pre-processing step prior to subsequent data analysis). However, there are important caveats to this type of usage (see FAQ for details).
For additional details on the code outputs, please see the documentation in GLMdenoisedata.m.
Example scripts
Example dataset. The example scripts make use of an example dataset. The example dataset consists of fMRI measurements in human visual cortex at 3T using gradient-echo, isotropic 2.5-mm voxels, and a TR (sampling rate) of approximately 1.3 s (1.337702 s). The dataset has a dimensionality of 64 voxels x 64 voxels x 4 slices. (The original dataset contains 22 slices, but we have extracted slices 9–12 to make the dataset smaller in size.) The data have been pre-processed, including discarding the first few volumes of each run, slice time correction, motion correction, and spatial undistortion based on fieldmap measurements. Voxels for which a complete set of data is not available (e.g. due to head motion) have been set such that their time-series consist of all 0s. The following variables are contained in the dataset file:
- 'design' - This is a cell vector of dimensions 1 x 10. Each element of the cell vector is a design matrix of dimensions 265 time points x 35 conditions. The format is double and values are either 0 or 1.
- 'data' - This is a cell vector of dimensions 1 x 10. Each element of the cell vector is an fMRI run of dimensions 64 voxels x 64 voxels x 4 slices x 265 time points. The format is single (to save on memory usage).
- 'stimdur' - This is a scalar (3), indicating the duration of each condition in seconds.
- 'tr' - This is a scalar (1.337702), indicating the sampling rate (TR) in seconds.
Example scripts. The following links are to HTML pages automatically produced by the MATLAB publish command.
Frequently asked questions (FAQ)
- "How does GLMdenoise compare to incorporating motion regressors, physiologically-derived regressors (e.g. RETROICOR), etc., into a standard GLM analysis?"
This issue is discussed in the manuscript. An advantage of GLMdenoise is that it subsumes these sorts of effects and is therefore more general. For example, if some portion of the noise can be modeled away using motion parameters, then this in theory can be derived directly from the data using the GLMdenoise technique. Furthermore, GLMdenoise has the advantage of being self-calibrating. Including nuisance regressors in a GLM may actually degrade accuracy due to overfitting. GLMdenoise uses cross-validation and therefore automatically determines the appropriate number of nuisance regressors to include.
As shown in the manuscript, GLMdenoise outperforms the strategy of including motion parameters as nuisance regressors and the strategy of using physiological regressors (RETROICOR). Note that one caveat is that we are under the working assumption that subject motion is not reliably correlated with the task. The case of task-correlated motion is complicated and dealing with this problem effectively is outside the scope of the GLMdenoise technique.
On a practical note, since GLMdenoise already deals with a variety of noise effects, you do not need to (and you should not) attempt to supply noise regressors (e.g. motion parameters, drift parameters) to GLMdenoise. All you need to supply is a specification of the experimental design matrix.
- "I have read about ICA (independent components analysis) as a potential denoising technique. How does GLMdenoise compare?"
At some level, GLMdenoise and ICA are similar techniques, as they are both data-driven techniques that attempt to isolate and remove noise. However, ICA relies on certain assumptions for identifying different components of the data, and there is no guarantee that the signal (i.e. portions of the data related to the task) and the noise are accurately identified. GLMdenoise explicitly estimates the portion of the data that is related to the experiment (through a GLM analysis) and uses cross-validation to verify that these estimates are accurate. Of course, depending on the nature of the data and the goals of the analysis, ICA may have its own advantages (e.g. the fact that it is model-free may be an advantage in cases where a model of the experiment is unknown). A direct comparison of ICA and GLMdenoise is provided in the manuscript.
- "The conditions in my experiment have different durations (e.g., a 2-s stimulus, a 6-s stimulus, etc.). How can I use GLMdenoise to deal with this situation?"
The underlying model of GLMdenoise is that the design matrix (which consists of 1s that indicate the onsets of different conditions) is convolved with a timecourse (the HRF) to obtain the predicted effects of the various conditions on the BOLD signal. To characterize the fact that the response to a 6-s stimulus is expected to have a longer duration than the response to a 2-s stimulus, we can simply code the design matrix appropriately. For example, suppose the TR (data sampling rate) is 1 s. Then, the column in the design matrix corresponding to the 2-s stimulus can be simply coded as something like [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0] where 1 indicates the onset of the 2-s stimulus. The column in the design matrix corresponding to the 6-s stimulus can be coded as something like [0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0] where the first 1 indicates the onset of the 6-s stimulus and the two additional 1s occur 2 s and 4 s after the initial onset. The idea is that there is a fixed timecourse associated with 2-s of stimulation and that the response to the 6-s stimulus is assumed to be obtained by shifting and summing this timecourse appropriately. Be aware that the interpretation of the beta weights associated with the 2-s and 6-s conditions is a little tricky --- the beta weight associated with the 2-s stimulus is simple (as it is just a scale factor that is applied to the HRF (which is normalized to peak at one)), but the beta weight associated with the 6-s stimulus is more complicated. The predicted response to the 6-s stimulus is given by first shifting and summing the HRF and then scaling by the beta weight. Because of the shifting and summing, the timecourse that is associated with the 2-s stimulus (before beta weight scaling) differs in shape and amplitude from the timecourse that is associated with the 6-s stimulus (before beta weight scaling).
A totally different strategy for dealing with different duration conditions is to use a finite impulse response (FIR) model (see example scripts).
- "If I want to model events with different intensity, can I just replace the 1s in the design matrix with the
appropriate fractions?"
Yes, the design matrix does not have to consist of only 1s and 0s, but can include fractional values as well. The case of fractional values is treated exactly as expected --- for example, if one event onset is coded as 1.0 and another onset of that same event is coded as 0.5, then the model enforces the expectation that the amplitude of the response resulting from the second onset will be half as large as the amplitude of the response resulting from the first onset.
- "I typically spatially smooth my data. Is that compatible with GLMdenoise?"
Yes, it is compatible, and you can treat smoothing as a pre-processing step that is applied to the data prior to the application of GLMdenoise.
One benefit of smoothing is that it generally improves SNR. The GLMdenoise technique also improves SNR. On datasets that I have experimented with, I have found that GLMdenoise improves SNR both on unsmoothed and smoothed data. This makes sense since the type of noise removed by GLMdenoise (large-scale correlated noise) is different than the noise removed by smoothing (local uncorrelated noise).
- "I like to use multivariate pattern analysis (i.e. pattern classifiers) on my fMRI data. Can GLMdenoise help me?"
Yes, it can. The intuition is that more information can be extracted (i.e. decoded) from the data if the data have less noise. However, it is important to think carefully about the issue of how exactly a given pattern analysis model is trained and tested in a given experiment. When applying GLMdenoise to a set of runs, all runs in that set contribute to the final results, and so the runs are no longer independent.
One approach for separating training and testing data in the context of a multivariate pattern analysis is to cross-validate across runs. For example, suppose 10 runs of data are collected. A model can be trained from 5 runs and then tested on the other 5 runs. To integrate this approach with GLMdenoise, one could separately apply GLMdenoise to each set of 5 runs. This way, there is no interaction between the data in the two sets of runs.
An additional issue that may be interesting to consider in the context of multivariate pattern analysis is that GLMdenoise is based on the concept of removing correlated noise across voxels. When applying pattern analysis to a set of voxels, the results will be affected by the extent to which noise is correlated across voxels and the extent to which these noise correlations are taken into account by the pattern analysis. After applying GLMdenoise to a set of data, not only is the overall level of noise likely to be reduced, but also the noise correlations are likely to be reduced.
- "Can I change the HRF model?"
For simplicity, the default mode of operation for GLMdenoise is to assume that there is a common response timecourse (the HRF) that is shared across all conditions and voxels. The shape of this HRF is optimized to best fit the data (but can also be pre-specified and fixed by the user).
A different approach is to attempt to tailor the shape of the HRF to individual voxels (or groups of voxels) (see example scripts).
Yet another approach is to fully tailor the shape of the HRF for all conditions and all voxels. This can be done by using the finite impulse response (FIR) model (see example scripts).
The GLMdenoise approach is applicable irrespective of the particular HRF model used (for example, gains in cross-validation performance and SNR can still be obtained if one decides to use an FIR model).
- "What if I have only one run of data?"
GLMdenoise needs multiple runs so that it can perform cross-validation. So, if you have only one run, you will not be able to use GLMdenoise. However, if you split a run into two smaller successive runs before calling GLMdenoise, then that may be a potential solution. One issue with this strategy is that the experiment-related effects from the end of the first small run may spill over to the beginning of the next small run, and if you split a run before calling GLMdenoise, then GLMdenoise will not know about these spillover effects. Whether or not the spillover is a significant problem depends on the situation (e.g. how much spillover, how much data is collected, etc.), so this is something to think about.
- "My experiment consists of trials that are all unique (e.g., a given trial is never repeated elsewhere in the experiment (or I want to estimate the response to each trial separately)). Can I use GLMdenoise?"
The code relies on cross-validation, and if conditions are never repeated, then a GLM cannot predict left-out data. So, GLMdenoise cannot be applied in this situation.
- "The idea that GLMdenoise can return the original data but with noise components removed (the 'denoised data') is nice and I am using the code that way. However, I have noticed some weird issues in my results."
If you choose to apply GLMdenoise using the "data-to-data-transformation" approach, it is very important to keep in mind that quantities that depend on error estimates (e.g. t-values, p-values) derived from the denoised data will be invalid. To see why this is the case, imagine that the code just returned "noiseless" data, i.e. data consisting of just the estimated experimental effects. Then, using some post-hoc analysis of the denoised data (e.g. another GLM), the estimated noise level will be zero and t-values will be infinite, and this is clearly absurd. One way forward would be to just use the denoised data to derive best possible estimates of the main quantities of interest (e.g. beta weights) and to infer error on these estimates by looking across different sets of data. For example, the error can be inferred by looking at how the quantities of interest change in an independently analyzed set of runs, how the quantities change across subjects, etc. Another way forward is to abandon the "data-to-data-transformation" approach and just use the estimates of beta weights and standard error provided by GLMdenoise.
- "In looking at the scatter plots of cross-validation performance for different numbers of PCs, I find that performance on average improves across voxels, but some voxels degrade in performance. Is this a concern?"
The metric of cross-validated R^{2}, like any metric, is sensitive to the amount of data that goes into the calculation of the metric. With sufficient amounts of data, random fluctuations (i.e. noise) in the left-out data will tend to average out in the calculation of the metric and the metric will tend to be robust. With small amounts of data, however, the metric may be noisy. Thus, there are two potential explanations for why cross-validation performance varies across voxels. One is that the underlying improvements are indeed consistent across voxels, and it is simply noise in the left-out data that is inducing variability in the observed cross-validation performance. The other is that voxels have real variability in their characteristics (e.g. some voxels have different denoising needs compared to other voxels). The reality is probably some combination of these two explanations, although we know a priori that with large datasets (i.e. many TRs, many runs), the first explanation has less influence. If you are particularly concerned with optimizing performance for a particular set of voxels, you can use opt.pcR2cutoffmask to specify the voxels to consider in the selection of the number of PCs to use.
- "I noticed that some voxels outside the brain have positive cross-validation values. Why is this?"
As noted in the previous point, because noise affects the data used for cross-validation, cross-validated R^{2} values are susceptible to noise. The threshold of a cross-validated R^{2} value of 0% (which is used in the GLMdenoise technique at several points) is just a threshold, and false positives are still possible. That is, a voxel with no actual relationship to the experiment may, due to chance, have a cross-validated R^{2} value greater than 0%.
- "The cross-validated R^{2} values in my experiment are low (e.g. 0-5%). Should I be concerned?"
The size of the cross-validated R^{2} values in any given dataset depends on a variety of factors, including the amount of noise (thermal, physiological, etc.) in the data, the strength of the evoked BOLD response (which depends on both the underlying neural activity magnitude as well as hemodynamic coupling characteristics in each voxel), and the experimental design (e.g. long-duration conditions will tend to elicit larger BOLD changes than short-duration conditions). In most circumstances, the absolute magnitude of the R^{2 }values is probably not of intrinsic interest; rather, it is the estimates of the evoked responses (and their reliability) that is of interest. So, having low R^{2} values is in itself not necessarily a cause for concern (although it can be informative for judging general SNR levels).
To clarify the way cross-validated R^{2 }values work, suppose you have a perfect model (i.e. you have infinite data to estimate the parameters of a GLM whose assumptions are valid). Even though this model is perfect, the model may still have low cross-validated R^{2} values. This is because even a perfect model cannot predict noise in unseen data. One way to increase R^{2 }values is to reduce noise in the data being predicted, and a simple way to do this is to acquire two (or more) runs with identical experimental conditions (including identical timing) and then average these runs together before proceeding to the GLM analysis. For example, you could collect 5 runs, repeat these 5 runs exactly, average corresponding pairs of runs together, and then submit the averaged 5 runs to GLMdenoise.
- "How important is the removal of good voxels (voxels with cross-validated R^{2} greater than 0%) from the noise pool? Can we just skip this step?"
You can, in fact, experiment with this aspect of GLMdenoise by setting the opt.brainR2 parameter to, say, 101, which will allow all voxels to enter the noise pool. The reason that it is important to exclude voxels with signals related to the experiment is that otherwise, the noise regressors that are derived will potentially contain signals related to the experiment, and this is likely to diminish the performance gains obtained by introducing noise regressors into the GLM model. Indeed, a comparison of results obtained with and without exclusion of good voxels is provided in the GLMdenoise paper.
- "Can I use GLMdenoise to denoise resting-state fMRI data?"
Central to GLMdenoise is the idea that we have a signal of interest (i.e. task-related responses) and this signal can be estimated and cross-validated. This is not the case (at least in any straightforward sense) in resting-state fMRI, so GLMdenoise cannot be used for resting-state fMRI.
Other notes
Error-checking user inputs. There are currently no safeguards on what happens if the user provides inputs in the wrong format.
Singular matrices. In some cases (e.g., a finite impulse response model with many free parameters and low amounts of data), the design matrix may be singular. The code intentionally crashes if a singular design matrix is encountered.
Reoptimize the noise pool. It is possible that voxels in the noise pool (that have cross-validated R^{2 } less than 0%) will, after denoising, have cross-validation R^{2 } greater than 0%. In theory, one might suppose that a new noise pool can be constructed (based on the new identification of what voxels are signal and what voxels are noise), new noise regressors can be derived, new models can be fit, etc., and the whole process can be iterated until convergence. This is an interesting idea, but it is doubtful that this would result in any substantial changes/improvements. The reason is that the proportion of voxels that escape the noise pool is probably very low in any given experiment and their time-series data are dominated by noise anyway; thus, removing those voxels from the noise pool will probably not substantially change the noise regressors that are derived.
Error estimation. To obtain error bars on beta weights, GLMdenoise uses a bootstrapping procedure (i.e. draw runs with replacement from the available set of runs, estimate parameters, and repeat this process a large number of times). Since this process is stochastic, error estimates on beta weights may vary a bit, and in particular, may vary across conditions. If, in subsequent analyses, you wish to convert each voxel's beta weights into t-units, instead of dividing each beta weight by its standard error, you may wish to first calculate the average standard error across conditions (for each voxel) and divide all beta weights (associated with that voxel) by this value. This, of course, presumes that the noise is roughly constant across conditions, but it does have the benefit that random fluctuations in error estimates across conditions will not affect the final values obtained for a voxel. GLMdenoise also provides an option for obtaining parametric GLM fits and associated error estimates.
Multiple sessions. If you are attempting to analyze functional data from different scan sessions, it may be useful to apply GLMdenoise to each session individually and then pool the results across sessions. The reason is that signal and noise characteristics may vary substantially from session to session.
Known issues and caveats
Memory usage. The code consists of large-scale matrix operations applied to many voxels simultaneously. This means that the code is fast but memory-intensive. Currently, there are no internal checks on the amount of memory used by the code. Thus, there is potential for the code to hit the maximum memory available on your machine, thereby causing swapping (and unreasonably slow performance). Memory usage is particularly dependent on the number of bootstraps performed (so you may wish to keep the number of bootstraps low, or set the number of bootstraps to zero). To reduce memory usage, you may also want to set opt.denoisespec to {}, make sure your data is prepare in single format, and/or crop your fMRI volumes. The easiest solution may be to buy more RAM!
Unbalanced designs. A presumption of GLMdenoise is that in any given dataset, there are multiple runs and conditions repeat across runs. In addition, there is some presumption that runs are more or less "similar". To understand what this last condition means, let us consider an extreme case. Suppose that some of the runs in a dataset are extremely short (e.g. 30 time points) whereas the remainder of the runs are long (e.g. 300 time points). In this case, we expect some degree of inconsistency with regards to the appropriate amount of denoising to perform across the two types of runs. That is, when training the model on the long runs, the number of PCs that result in optimal performance will probably be on high side, whereas when training the model on the short runs, the number of PCs that result in optimal performance will probably be on the low side (i.e. overfitting is likely).
Unbalanced designs and bootstrapping. Unbalanced designs can interact in certain ways with the bootstrapping procedure. For example, suppose that you have two run types such that a given set of conditions occur in one run type and a non-overlapping set of conditions occur in the other run type. In this case, the bootstrap procedure used at the end of the GLMdenoise algorithm may encounter a bootstrap sample that (by chance) does not include any occurrence of a given condition (for example, suppose that all draws in a bootstrap sample were obtained from runs of the first run type). If the code encounters a case where there are no occurrences of a given condition, the code will simply assign a zero weight for those cases. A solution for the issue of unbalanced designs and bootstrapping is to make appropriate use of the opt.bootgroups variable (that is, you can ensure that the bootstrap sample is balanced with respect to the two types of runs).
Unbalanced designs and cross-validation. The issue of unbalanced designs can also crop up in the leave-one-run-out cross-validation procedure used in GLMdenoise. For example, suppose all occurrences of a certain set of conditions occur in just one of the runs in a dataset. Then, when that run is left out in the cross-validation procedure, then no occurrences of those conditions are available to estimate the beta weights corresponding to those conditions. When this is encountered, the code will simply assign a zero weight for those conditions, thereby resolving the problem (although the situation is a bit strange since the prediction for the left-out run is always a time-series consisting of 0s and so denoising will never appear to be useful for that cross-validation iteration).
Bootstrap smaller chunks of data. The strategy for estimating error bars on model parameters is to bootstrap runs (i.e., draw with replacement from the original set of runs). With small numbers of runs, this strategy might result in sub-optimal estimates. One potential strategy is to split runs into small chunks (e.g. 1-min chunks) and then bootstrap these chunks. This strategy may produce better bootstrap results, and still largely respects the fact that the noise is not independent from time point to time point.