Coverage for nilearn/maskers/multi_nifti_maps_masker.py: 28%

51 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-20 10:58 +0200

1"""Transformer for computing ROI signals of multiple 4D images.""" 

2 

3import itertools 

4 

5from joblib import Parallel, delayed 

6from sklearn.utils.estimator_checks import check_is_fitted 

7 

8from nilearn._utils import fill_doc 

9from nilearn._utils.niimg_conversions import iter_check_niimg 

10from nilearn._utils.tags import SKLEARN_LT_1_6 

11from nilearn.maskers.base_masker import prepare_confounds_multimaskers 

12from nilearn.maskers.nifti_maps_masker import NiftiMapsMasker 

13from nilearn.typing import NiimgLike 

14 

15 

16@fill_doc 

17class MultiNiftiMapsMasker(NiftiMapsMasker): 

18 """Class for extracting data from multiple Niimg-like objects \ 

19 using maps of potentially overlapping brain regions. 

20 

21 MultiNiftiMapsMasker is useful when data from overlapping volumes 

22 and from different subjects should be extracted (contrary to 

23 :class:`nilearn.maskers.NiftiMapsMasker`). 

24 

25 Use case: 

26 summarize brain signals from several subjects 

27 from large-scale networks obtained by prior PCA or :term:`ICA`. 

28 

29 .. note:: 

30 Inf or NaN present in the given input images are automatically 

31 put to zero rather than considered as missing data. 

32 

33 For more details on the definitions of maps in Nilearn, 

34 see the :ref:`region` section. 

35 

36 Parameters 

37 ---------- 

38 maps_img : 4D niimg-like object or None, default=None 

39 See :ref:`extracting_data`. 

40 Set of continuous maps. One representative time course per map is 

41 extracted using least square regression. 

42 

43 mask_img : 3D niimg-like object, optional 

44 See :ref:`extracting_data`. 

45 Mask to apply to regions before extracting signals. 

46 

47 allow_overlap : :obj:`bool`, default=True 

48 If False, an error is raised if the maps overlaps (ie at least two 

49 maps have a non-zero value for the same voxel). 

50 %(smoothing_fwhm)s 

51 %(standardize_maskers)s 

52 %(standardize_confounds)s 

53 high_variance_confounds : :obj:`bool`, default=False 

54 If True, high variance confounds are computed on provided image with 

55 :func:`nilearn.image.high_variance_confounds` and default parameters 

56 and regressed out. 

57 %(detrend)s 

58 %(low_pass)s 

59 %(high_pass)s 

60 %(t_r)s 

61 

62 %(dtype)s 

63 

64 resampling_target : {"data", "mask", "maps", None}, default="data" 

65 Gives which image gives the final shape/size: 

66 

67 - "data" means the atlas is resampled to the shape of the data if 

68 needed 

69 - "mask" means the maps_img and images provided to fit() are 

70 resampled to the shape and affine of mask_img 

71 - "maps" means the mask_img and images provided to fit() are 

72 resampled to the shape and affine of maps_img 

73 - None means no resampling: if shapes and affines do not match, 

74 a ValueError is raised. 

75 

76 

77 %(memory)s 

78 

79 %(memory_level)s 

80 

81 %(n_jobs)s 

82 

83 %(verbose0)s 

84 

85 reports : :obj:`bool`, default=True 

86 If set to True, data is saved in order to produce a report. 

87 

88 %(cmap)s 

89 default="CMRmap_r" 

90 Only relevant for the report figures. 

91 

92 %(clean_args)s 

93 

94 %(masker_kwargs)s 

95 

96 Attributes 

97 ---------- 

98 maps_img_ : :obj:`nibabel.nifti1.Nifti1Image` 

99 The maps mask of the data. 

100 

101 %(nifti_mask_img_)s 

102 

103 n_elements_ : :obj:`int` 

104 The number of overlapping maps in the mask. 

105 This is equivalent to the number of volumes in the mask image. 

106 

107 .. versionadded:: 0.9.2 

108 

109 Notes 

110 ----- 

111 If resampling_target is set to "maps", every 3D image processed by 

112 transform() will be resampled to the shape of maps_img. It may lead to a 

113 very large memory consumption if the voxel number in maps_img is large. 

114 

115 See Also 

116 -------- 

117 nilearn.maskers.NiftiMasker 

118 nilearn.maskers.NiftiLabelsMasker 

119 nilearn.maskers.NiftiMapsMasker 

120 

121 """ 

122 

123 # memory and memory_level are used by CacheMixin. 

124 

125 def __init__( 

126 self, 

127 maps_img=None, 

128 mask_img=None, 

129 allow_overlap=True, 

130 smoothing_fwhm=None, 

131 standardize=False, 

132 standardize_confounds=True, 

133 high_variance_confounds=False, 

134 detrend=False, 

135 low_pass=None, 

136 high_pass=None, 

137 t_r=None, 

138 dtype=None, 

139 resampling_target="data", 

140 memory=None, 

141 memory_level=0, 

142 verbose=0, 

143 reports=True, 

144 cmap="CMRmap_r", 

145 n_jobs=1, 

146 clean_args=None, 

147 **kwargs, 

148 ): 

149 self.n_jobs = n_jobs 

150 super().__init__( 

151 maps_img, 

152 mask_img=mask_img, 

153 allow_overlap=allow_overlap, 

154 smoothing_fwhm=smoothing_fwhm, 

155 standardize=standardize, 

156 standardize_confounds=standardize_confounds, 

157 high_variance_confounds=high_variance_confounds, 

158 detrend=detrend, 

159 low_pass=low_pass, 

160 high_pass=high_pass, 

161 t_r=t_r, 

162 dtype=dtype, 

163 resampling_target=resampling_target, 

164 memory=memory, 

165 memory_level=memory_level, 

166 verbose=verbose, 

167 reports=reports, 

168 cmap=cmap, 

169 clean_args=clean_args, 

170 **kwargs, 

171 ) 

172 

173 def __sklearn_tags__(self): 

174 """Return estimator tags. 

175 

176 See the sklearn documentation for more details on tags 

177 https://scikit-learn.org/1.6/developers/develop.html#estimator-tags 

178 """ 

179 # TODO 

180 # get rid of if block 

181 # bumping sklearn_version > 1.5 

182 if SKLEARN_LT_1_6: 

183 from nilearn._utils.tags import tags 

184 

185 return tags(masker=True, multi_masker=True) 

186 

187 from nilearn._utils.tags import InputTags 

188 

189 tags = super().__sklearn_tags__() 

190 tags.input_tags = InputTags(masker=True, multi_masker=True) 

191 return tags 

192 

193 @fill_doc 

194 def transform_imgs( 

195 self, imgs_list, confounds=None, n_jobs=1, sample_mask=None 

196 ): 

197 """Extract signals from a list of 4D niimgs. 

198 

199 Parameters 

200 ---------- 

201 %(imgs)s 

202 Images to process. 

203 

204 %(confounds_multi)s 

205 

206 %(n_jobs)s 

207 

208 %(sample_mask_multi)s 

209 

210 Returns 

211 ------- 

212 %(signals_transform_imgs_multi_nifti)s 

213 

214 """ 

215 # We handle the resampling of maps and mask separately because the 

216 # affine of the maps and mask images should not impact the extraction 

217 # of the signal. 

218 

219 check_is_fitted(self) 

220 

221 niimg_iter = iter_check_niimg( 

222 imgs_list, 

223 ensure_ndim=None, 

224 atleast_4d=False, 

225 memory=self.memory, 

226 memory_level=self.memory_level, 

227 ) 

228 

229 confounds = prepare_confounds_multimaskers(self, imgs_list, confounds) 

230 

231 if sample_mask is None: 

232 sample_mask = itertools.repeat(None, len(imgs_list)) 

233 elif len(sample_mask) != len(imgs_list): 

234 raise ValueError( 

235 f"number of sample_mask ({len(sample_mask)}) unequal to " 

236 f"number of images ({len(imgs_list)})." 

237 ) 

238 

239 func = self._cache(self.transform_single_imgs) 

240 

241 region_signals = Parallel(n_jobs=n_jobs)( 

242 delayed(func)(imgs=imgs, confounds=cfs, sample_mask=sms) 

243 for imgs, cfs, sms in zip(niimg_iter, confounds, sample_mask) 

244 ) 

245 return region_signals 

246 

247 @fill_doc 

248 def transform(self, imgs, confounds=None, sample_mask=None): 

249 """Apply mask, spatial and temporal preprocessing. 

250 

251 Parameters 

252 ---------- 

253 imgs : Niimg-like object, or a :obj:`list` of Niimg-like objects 

254 See :ref:`extracting_data`. 

255 Data to be preprocessed 

256 

257 %(confounds_multi)s 

258 

259 %(sample_mask_multi)s 

260 

261 Returns 

262 ------- 

263 %(signals_transform_multi_nifti)s 

264 

265 """ 

266 check_is_fitted(self) 

267 

268 if not (confounds is None or isinstance(confounds, list)): 

269 raise TypeError( 

270 "'confounds' must be a None or a list. " 

271 f"Got {confounds.__class__.__name__}." 

272 ) 

273 if not (sample_mask is None or isinstance(sample_mask, list)): 

274 raise TypeError( 

275 "'sample_mask' must be a None or a list. " 

276 f"Got {sample_mask.__class__.__name__}." 

277 ) 

278 if isinstance(imgs, NiimgLike): 

279 if isinstance(confounds, list): 

280 confounds = confounds[0] 

281 if isinstance(sample_mask, list): 

282 sample_mask = sample_mask[0] 

283 return super().transform( 

284 imgs, confounds=confounds, sample_mask=sample_mask 

285 ) 

286 

287 return self.transform_imgs( 

288 imgs, 

289 confounds=confounds, 

290 sample_mask=sample_mask, 

291 n_jobs=self.n_jobs, 

292 ) 

293 

294 @fill_doc 

295 def fit_transform(self, imgs, y=None, confounds=None, sample_mask=None): 

296 """ 

297 Fit to data, then transform it. 

298 

299 Parameters 

300 ---------- 

301 imgs : Niimg-like object, or a :obj:`list` of Niimg-like objects 

302 See :ref:`extracting_data`. 

303 Data to be preprocessed 

304 

305 y : None 

306 This parameter is unused. It is solely included for scikit-learn 

307 compatibility. 

308 

309 %(confounds_multi)s 

310 

311 %(sample_mask_multi)s 

312 

313 .. versionadded:: 0.8.0 

314 

315 Returns 

316 ------- 

317 %(signals_transform_multi_nifti)s 

318 """ 

319 return self.fit(imgs, y=y).transform( 

320 imgs, confounds=confounds, sample_mask=sample_mask 

321 )