@@ -1139,15 +1139,29 @@ def combine_mask_files(mask_files, mask_method=None, mask_index=None):
1139
1139
return [img ]
1140
1140
1141
1141
1142
- def compute_noise_components (imgseries , mask_images , num_components ,
1143
- filter_type , degree , period_cut , repetition_time ):
1142
+ def compute_noise_components (imgseries , mask_images , num_components = 0.5 ,
1143
+ filter_type = False , degree = 0 , period_cut = 128 ,
1144
+ repetition_time = None , failure_mode = 'error' ):
1144
1145
"""Compute the noise components from the imgseries for each mask
1145
1146
1146
- imgseries: a nibabel img
1147
- mask_images: a list of nibabel images
1148
- num_components: number of noise components to return
1149
- filter_type: type off filter to apply to time series before computing
1150
- noise components.
1147
+ Parameters
1148
+ ----------
1149
+ imgseries: nibabel NIfTI object
1150
+ Time series data to be decomposed.
1151
+ mask_images: list
1152
+ List of nibabel images. Time series data from `img_series` is subset
1153
+ according to the spatial extent of each mask, and the subset data is
1154
+ then decomposed using principal component analysis. Masks should be
1155
+ coextensive with either anatomical or spatial noise ROIs.
1156
+ num_components: float
1157
+ Number of noise components to return. If this is a decimal value
1158
+ between 0 and 1, then `create_noise_components` will instead return
1159
+ the smallest number of components necessary to explain the indicated
1160
+ fraction of variance. If `num_components` is -1, then all
1161
+ components will be returned.
1162
+ filter_type: str
1163
+ Type of filter to apply to time series before computing
1164
+ noise components.
1151
1165
'polynomial' - Legendre polynomial basis
1152
1166
'cosine' - Discrete cosine (DCT) basis
1153
1167
False - None (mean-removal only)
@@ -1158,16 +1172,20 @@ def compute_noise_components(imgseries, mask_images, num_components,
1158
1172
period_cut: minimum period (in sec) for DCT high-pass filter
1159
1173
repetition_time: time (in sec) between volume acquisitions
1160
1174
1161
- returns:
1162
-
1163
- components: a numpy array
1164
- basis: a numpy array containing the (non-constant) filter regressors
1165
-
1175
+ Outputs
1176
+ -------
1177
+ components: numpy array
1178
+ Numpy array containing the requested set of noise components
1179
+ basis: numpy array
1180
+ Numpy array containing the (non-constant) filter regressors
1181
+ metadata: dict(numpy array)
1182
+ Dictionary of eigenvalues, fractional explained variances, and
1183
+ cumulative explained variances.
1166
1184
"""
1167
1185
components = None
1168
1186
basis = np .array ([])
1169
- for img in mask_images :
1170
- mask = img .get_data ().astype (np .bool )
1187
+ for i , img in enumerate ( mask_images ) :
1188
+ mask = img .get_data ().astype (np .bool ). squeeze ()
1171
1189
if imgseries .shape [:3 ] != mask .shape :
1172
1190
raise ValueError (
1173
1191
'Inputs for CompCor, timeseries and mask, do not have '
@@ -1201,20 +1219,47 @@ def compute_noise_components(imgseries, mask_images, num_components,
1201
1219
# "The covariance matrix C = MMT was constructed and decomposed into its
1202
1220
# principal components using a singular value decomposition."
1203
1221
try :
1204
- u , _ , _ = fallback_svd (M , full_matrices = False )
1222
+ u , s , _ = fallback_svd (M , full_matrices = False )
1205
1223
except np .linalg .LinAlgError :
1206
1224
if self .inputs .failure_mode == 'error' :
1207
1225
raise
1208
- u = np .ones ((M .shape [0 ], num_components ), dtype = np .float32 ) * np .nan
1226
+ if num_components >= 1 :
1227
+ u = np .empty ((M .shape [0 ], num_components ),
1228
+ dtype = np .float32 ) * np .nan
1229
+ else :
1230
+ continue
1231
+
1232
+ variance_explained = np .array ([value ** 2 / np .sum (s ** 2 ) for value in s ])
1233
+ cumulative_variance_explained = np .cumsum (variance_explained )
1234
+ if 0 < num_components < 1 :
1235
+ num_components = np .searchsorted (cumulative_variance_explained ,
1236
+ num_components ) + 1
1237
+ elif num_components == - 1 :
1238
+ num_components = len (s )
1209
1239
if components is None :
1210
1240
components = u [:, :num_components ]
1241
+ metadata = {
1242
+ 'mask' : np .array ([i ] * len (s )),
1243
+ 'singular_values' : s ,
1244
+ 'variance_explained' : variance_explained ,
1245
+ 'cumulative_variance_explained' : cumulative_variance_explained
1246
+ }
1211
1247
else :
1212
1248
components = np .hstack ((components , u [:, :num_components ]))
1213
- if components is None and num_components > 0 :
1249
+ metadata ['mask' ] = np .hstack ((metadata ['mask' ], [i ] * len (s )))
1250
+ metadata ['singular_values' ] = (
1251
+ np .hstack ((metadata ['singular_values' ], s )))
1252
+ metadata ['variance_explained' ] = (
1253
+ np .hstack ((metadata ['variance_explained' ],
1254
+ variance_explained )))
1255
+ metadata ['cumulative_variance_explained' ] = (
1256
+ np .hstack ((metadata ['cumulative_variance_explained' ],
1257
+ cumulative_variance_explained )))
1258
+ if components is None and num_components != 0 :
1214
1259
if self .inputs .failure_mode == 'error' :
1215
1260
raise ValueError ('No components found' )
1216
1261
components = np .ones ((M .shape [0 ], num_components ), dtype = np .float32 ) * np .nan
1217
- return components , basis
1262
+ return components , basis , metadata
1218
1263
1219
1264
1220
1265
def _compute_tSTD (M , x , axis = 0 ):
0 commit comments