I’m working on denoising resting-state fMRI data by regressing out nuisance parameters (motion, WM, CSF, etc.), applying filtering, detrending, and performing scrubbing.
Specifically, I want to set the signal in scrubbed volumes to zero while z-scoring the remaining volumes. This approach aim to preserve the temporal structure of the signal, important for dynamic FC.
However, I’m unsure about the correct point in the pipeline to apply scrubbing. I’m currently using the following NiftiLabelsMasker in Nilearn to perform all preprocessing steps except scrubbing:
NiftiLabelsMasker.fit_transform() accepts a sample_mask parameter to do the scrubbing. I recommend using that parameter instead of scrubbing separately.
Can you clarify what you mean by setting the scrubbed volumes to zero while z-scoring the remaining volumes? Setting “bad” volumes to zero would affect the mean and standard deviation of the time series, so I wouldn’t recommend doing that.
“High-pass temporal filtering >0.013 Hz, (iv) regressing out confounds including the average signal of white matter and cerebrospinal fluid voxels as well as 24 motion parameters (translation and rotation in the three directions, in addition to their squares, derivatives, and squares of derivatives), and (v) scrubbing motion outliers, defined on the basis of root mean squared translation >0.25 mm. The scrubbing was done by setting the signal in motion outlier volumes to zero while Z-scoring the rest of the volumes. This approach, compared to discarding the motion outliers, preserves the temporal structure of the BOLD signal, which is important in calculating dynamic FC.”
The idea is to preserve the shape of the BOLD time series and maintain consistent dimensions of sliding-window functional connectivity dynamics matrices.
I’m not sure how to approach this without interfering with filtering or reintroducing noise to components that were already removed orthogonally.
You still don’t want to denoise the data using extreme volumes, so I’d recommend using the sample_mask parameter and then creating an uncensored version of the array out of NaNs.
Something like this maybe:
n_volumes = fmri_img.shape[3]
n_parcels = full_ts.shape[1]
# assumes sample_mask is boolean array with True = good and False = bad
low_motion_idx = np.where(sample_mask)[0]
uncensored_array = np.full((n_volumes, n_parcels), np.nan)
uncensored_array[low_motion_idx, :] = full_ts
I would strongly discourage using 0s in the array, because those are actual values that would skew the correlations. You should be able to adapt the sliding-window FC code to work with NaNs though.
If I understand your suggestion correctly:
I should run the masker using the sample_mask to exclude bad volumes (standard scrubbing). Then, reintroduce NaNs into the cleaned time series exactly at the positions of the excluded frames to preserve the original temporal structure. Finally, calculate FC and FCD while properly handling NaNs — i.e., ignoring them in the computations. Is that correct?