OSCR

Correspondence of large-scale functional brain network decline across aging mice and humans.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Materials and Methods › Mouse Data Acquisition and Preprocessing. ↔ rabies/confound_correction_pkg/confound_correction.py, lines 752 to 838 · score 0.70 · confound correction, motion parameters, spatial smoothing, NOise, filtering, EPI · Report a mismatch
  2. [2] § Materials and Methods › Human Data Acquisition and Preprocessing. ↔ rabies/confound_correction_pkg/confound_correction.py, lines 752 to 838 · score 0.53 · motion parameters, spatial smoothing, variance, regression, frames, global · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 838 lines · 41 KB · other · 2 matches

  1. from nipype.pipeline import engine as pe
  2. from nipype.interfaces import utility as niu
  3. from nipype.interfaces.base import (
  4. traits, TraitedSpec, BaseInterfaceInputSpec,
  5. File, BaseInterface
  6. )
  7. def init_confound_correction_wf(cr_opts, name="confound_correction_wf"):
  8. # confound_wf_head_start
  9. """
  10. This workflow applies the RABIES confound correction pipeline to preprocessed EPI timeseries. The correction steps are
  11. orchestrated in line with recommendations from human litterature:
  12. #1 - Compute and apply frame censoring mask (from FD and/or DVARS thresholds)
  13. #2 - If --match_number_timepoints is set, each scan is matched to the defined number of frames.
  14. #4 - Linear/Quadratic detrending of fMRI timeseries and nuisance regressors
  15. #4 - Apply ICA-AROMA.
  16. #5 - If frequency filtering and frame censoring are applied, simulate data in censored timepoints using the Lomb-Scargle periodogram,
  17. as suggested in Power et al. (2014, Neuroimage), for both the fMRI timeseries and nuisance regressors prior to filtering.
  18. #6 - As recommended in Lindquist et al. (2019, Human brain mapping), make the nuisance regressors orthogonal
  19. to the temporal frequency filter.
  20. #7 - Apply highpass and/or lowpass filtering on the fMRI timeseries (with simulated timepoints).
  21. #8 - Re-apply the frame censoring mask onto filtered fMRI timeseries and nuisance regressors, taking out the
  22. simulated timepoints. Edge artefacts from frequency filtering can also be removed as recommended in Power et al. (2014, Neuroimage).
  23. #9 - Apply confound regression using the selected nuisance regressors.
  24. #10 - Scaling of timeseries variance.
  25. #11 - Apply Gaussian spatial smoothing.
  26. References:
  27. Power, J. D., Barnes, K. A., Snyder, A. Z., Schlaggar, B. L., & Petersen, S. E. (2012). Spurious but systematic
  28. correlations in functional connectivity MRI networks arise from subject motion. Neuroimage, 59(3), 2142-2154.
  29. Power, J. D., Mitra, A., Laumann, T. O., Snyder, A. Z., Schlaggar, B. L., & Petersen, S. E. (2014). Methods to detect,
  30. characterize, and remove motion artifact in resting state fMRI. Neuroimage, 84, 320-341.
  31. Lindquist, M. A., Geuter, S., Wager, T. D., & Caffo, B. S. (2019). Modular preprocessing pipelines can reintroduce
  32. artifacts into fMRI data. Human brain mapping, 40(8), 2358-2376.
  33. Workflow:
  34. parameters
  35. cr_opts: command line interface parameters from confound_correction
  36. inputs
  37. bold_file: preprocessed EPI timeseries
  38. brain_mask: brain mask overlapping with EPI timeseries
  39. csf_mask: CSF mask overlapping with EPI timeseries
  40. motion_params_csv: CSV file with motion regressors
  41. FD_file: CSV file with the framewise displacement
  42. outputs
  43. cleaned_path: the cleaned EPI timeseries
  44. aroma_out: folder with outputs from ICA-AROMA
  45. VE_file: variance explained (R^2) from confound regression at each voxel
  46. STD_file: standard deviation on the cleaned EPI timeseries
  47. CR_STD_file: standard deviation on the confound timeseries modelled during confound regression
  48. random_CR_STD_file_path: variance fitted by random regressors during confound regression
  49. corrected_CR_STD_file_path: CR_STD_file after substracting the variance fitted by random
  50. regressors.
  51. frame_mask_file: CSV file which records which frame were censored
  52. CR_data_dict: dictionary object storing extra data computed during confound correction
  53. """
  54. # confound_wf_head_end
  55. workflow = pe.Workflow(name=name)
  56. inputnode = pe.Node(niu.IdentityInterface(fields=[
  57. 'bold_file', 'brain_mask', 'WM_mask', 'CSF_mask', 'vascular_mask', 'motion_params_csv', 'FD_file', 'raw_input_file']), name='inputnode')
  58. outputnode = pe.Node(niu.IdentityInterface(fields=[
  59. 'cleaned_path', 'aroma_out', 'VE_file', 'STD_file', 'CR_STD_file',
  60. 'random_CR_STD_file_path', 'corrected_CR_STD_file_path', 'frame_mask_file', 'CR_data_dict']), name='outputnode')
  61. clean_image_node = pe.Node(CleanImage(cr_opts=cr_opts),
  62. name='clean_image', mem_gb=3*cr_opts.scale_min_memory) # 3X memory as the timeseries is expanded into many arrays
  63. workflow.connect([
  64. (inputnode, clean_image_node, [
  65. ("bold_file", "bold_file"),
  66. ("brain_mask", "brain_mask_file"),
  67. ("WM_mask", "WM_mask_file"),
  68. ("CSF_mask", "CSF_mask_file"),
  69. ("vascular_mask", "vascular_mask_file"),
  70. ("raw_input_file", "raw_input_file"),
  71. ("motion_params_csv", "motion_params_csv"),
  72. ("FD_file", "FD_file"),
  73. ]),
  74. (clean_image_node, outputnode, [
  75. ("cleaned_path", "cleaned_path"),
  76. ("VE_file_path", "VE_file"),
  77. ("STD_file_path", "STD_file"),
  78. ("CR_STD_file_path", "CR_STD_file"),
  79. ("random_CR_STD_file_path", "random_CR_STD_file_path"),
  80. ("corrected_CR_STD_file_path", "corrected_CR_STD_file_path"),
  81. ("frame_mask_file", "frame_mask_file"),
  82. ("data_dict", "CR_data_dict"),
  83. ("aroma_out", "aroma_out"),
  84. ]),
  85. ])
  86. return workflow
  87. class CleanImageInputSpec(BaseInterfaceInputSpec):
  88. raw_input_file = File(exists=True, mandatory=True,
  89. desc="The raw EPI scan before preprocessing.")
  90. bold_file = File(exists=True, mandatory=True,
  91. desc="Timeseries to denoise.")
  92. brain_mask_file = File(exists=True, mandatory=True,
  93. desc="Brain mask.")
  94. WM_mask_file = traits.Any(mandatory=True,
  95. desc="WM mask.")
  96. CSF_mask_file = traits.Any(mandatory=True,
  97. desc="CSF mask.")
  98. vascular_mask_file = traits.Any(mandatory=True,
  99. desc="vascular mask.")
  100. FD_file = File(exists=True, mandatory=True,
  101. desc="CSV with framewise displacement.")
  102. motion_params_csv = File(exists=True, mandatory=True,
  103. desc="CSV with motion parameters.")
  104. cr_opts = traits.Any(
  105. exists=True, mandatory=True, desc="Processing specs.")
  106. class CleanImageOutputSpec(TraitedSpec):
  107. cleaned_path = File(exists=True, mandatory=True,
  108. desc="Cleaned timeseries.")
  109. VE_file_path = File(exists=True, mandatory=True,
  110. desc="Variance explained map from confound regression.")
  111. STD_file_path = File(exists=True, mandatory=True,
  112. desc="Temporal standard deviation map after confound correction, prior to standardization.")
  113. CR_STD_file_path = File(exists=True, mandatory=True,
  114. desc="Temporal standard deviation on predicted confound timeseries.")
  115. random_CR_STD_file_path = File(exists=False, mandatory=False,
  116. desc="Temporal standard deviation on predicted confound timeseries from random regressors.")
  117. corrected_CR_STD_file_path = File(exists=False, mandatory=False,
  118. desc="Same as CR_STD_file_path, but the variance explained by random regressors was substracted.")
  119. frame_mask_file = File(exists=True, mandatory=True,
  120. desc="Frame mask from temporal censoring.")
  121. data_dict = traits.Any(
  122. desc="A dictionary with key outputs.")
  123. aroma_out = traits.Any(
  124. desc="Output directory from ICA-AROMA.")
  125. class CleanImage(BaseInterface):
  126. '''
  127. Apply a flexible confound correction pipeline in line with recommendations from
  128. human litterature.
  129. #1 - Compute and apply frame censoring mask (from FD and/or DVARS thresholds)
  130. #2 - If --match_number_timepoints is set, each scan is matched to the defined number of frames.
  131. #4 - Linear/Quadratic detrending of fMRI timeseries and nuisance regressors
  132. #4 - Apply ICA-AROMA.
  133. #5 - If frequency filtering and frame censoring are applied, simulate data in censored timepoints using the Lomb-Scargle periodogram,
  134. as suggested in Power et al. (2014, Neuroimage), for both the fMRI timeseries and nuisance regressors prior to filtering.
  135. #6 - As recommended in Lindquist et al. (2019, Human brain mapping), make the nuisance regressors orthogonal
  136. to the temporal frequency filter.
  137. #7 - Apply highpass and/or lowpass filtering on the fMRI timeseries (with simulated timepoints).
  138. #8 - Re-apply the frame censoring mask onto filtered fMRI timeseries and nuisance regressors, taking out the
  139. simulated timepoints. Edge artefacts from frequency filtering can also be removed as recommended in Power et al. (2014, Neuroimage).
  140. #9 - If selected, compute the WM/CSF/vascular signal or aCompCorr and add to list of regressors. This is computed post-AROMA and filtering to
  141. minimize re-introduction of previously corrected signal fluctuations.
  142. #10 - Apply confound regression using the selected nuisance regressors.
  143. #11 - Scaling of timeseries.
  144. #12 - Apply Gaussian spatial smoothing.
  145. References:
  146. Power, J. D., Barnes, K. A., Snyder, A. Z., Schlaggar, B. L., & Petersen, S. E. (2012).
  147. Spurious but systematic correlations in functional connectivity MRI networks arise from subject motion. Neuroimage, 59(3), 2142-2154.
  148. Power, J. D., Mitra, A., Laumann, T. O., Snyder, A. Z., Schlaggar, B. L., & Petersen, S. E. (2014).
  149. Methods to detect, characterize, and remove motion artifact in resting state fMRI. Neuroimage, 84, 320-341.
  150. Lindquist, M. A., Geuter, S., Wager, T. D., & Caffo, B. S. (2019).
  151. Modular preprocessing pipelines can reintroduce artifacts into fMRI data. Human brain mapping, 40(8), 2358-2376.
  152. '''
  153. input_spec = CleanImageInputSpec
  154. output_spec = CleanImageOutputSpec
  155. def _run_interface(self, runtime):
  156. import os
  157. import numpy as np
  158. import pandas as pd
  159. import SimpleITK as sitk
  160. ### set null returns in case the workflow is interrupted
  161. empty_img = sitk.GetImageFromArray(np.empty([1,1]))
  162. empty_file = os.path.abspath('empty.nii.gz')
  163. sitk.WriteImage(empty_img, empty_file)
  164. setattr(self, 'cleaned_path', empty_file)
  165. setattr(self, 'VE_file_path', empty_file)
  166. setattr(self, 'STD_file_path', empty_file)
  167. setattr(self, 'CR_STD_file_path', empty_file)
  168. setattr(self, 'random_CR_STD_file_path', empty_file)
  169. setattr(self, 'corrected_CR_STD_file_path', empty_file)
  170. setattr(self, 'frame_mask_file', empty_file)
  171. setattr(self, 'data_dict', empty_file)
  172. setattr(self, 'aroma_out', empty_file)
  173. ###
  174. cr_opts = self.inputs.cr_opts
  175. from nipype import logging
  176. nipype_log = logging.getLogger('nipype.workflow')
  177. with np.errstate(invalid='ignore', divide='ignore'):
  178. cleaning_out = clean_image(
  179. input_bold = self.inputs.bold_file,
  180. brain_mask = self.inputs.brain_mask_file,
  181. WM_mask = self.inputs.WM_mask_file,
  182. CSF_mask = self.inputs.CSF_mask_file,
  183. vascular_mask = self.inputs.vascular_mask_file,
  184. FD_csv = self.inputs.FD_file,
  185. motion_params_csv=self.inputs.motion_params_csv,
  186. timeseries_interval = cr_opts.timeseries_interval,
  187. FD_censoring=cr_opts.frame_censoring['FD_censoring'],
  188. FD_threshold=cr_opts.frame_censoring['FD_threshold'],
  189. DVARS_censoring=cr_opts.frame_censoring['DVARS_censoring'],
  190. MSE_censoring=cr_opts.frame_censoring['MSE_censoring'],
  191. censoring_percent_exclusion=cr_opts.frame_censoring['censoring_percent_exclusion'],
  192. TR=cr_opts.TR,
  193. detrending_order=cr_opts.detrending['order'],
  194. detrending_time_interval=cr_opts.detrending['time_interval'],
  195. apply_ica_aroma=cr_opts.ica_aroma['apply'],
  196. ica_aroma_dim=cr_opts.ica_aroma['dim'],
  197. ica_aroma_random_seed=cr_opts.ica_aroma['random_seed'],
  198. match_number_timepoints=cr_opts.match_number_timepoints,
  199. highpass=cr_opts.highpass,
  200. lowpass=cr_opts.lowpass,
  201. edge_cutoff=cr_opts.edge_cutoff,
  202. nuisance_regressors = cr_opts.nuisance_regressors,
  203. generate_CR_null=cr_opts.generate_CR_null,
  204. scale_variance_voxelwise=cr_opts.scale_variance_voxelwise,
  205. image_scaling=cr_opts.image_scaling,
  206. keep_EPI_average=cr_opts.keep_EPI_average,
  207. smoothing_filter=cr_opts.smoothing_filter,
  208. slicewise_correction_direction=cr_opts.slicewise_correction_direction,
  209. nipype_log=nipype_log,
  210. )
  211. if cleaning_out is None:
  212. return runtime
  213. else:
  214. [timeseries_img,
  215. CR_data_dict,
  216. VE_spatial_map,
  217. STD_spatial_map,
  218. CR_STD_spatial_map,
  219. random_CR_STD_spatial_map,
  220. corrected_CR_STD_spatial_map] = cleaning_out
  221. '''
  222. Generate all the output files
  223. '''
  224. cr_out = os.getcwd()
  225. import pathlib # Better path manipulation
  226. filename_split = pathlib.Path(self.inputs.raw_input_file).name.rsplit(".nii")
  227. cleaned_path = cr_out+'/'+filename_split[0]+'_cleaned.nii.gz'
  228. sitk.WriteImage(timeseries_img, cleaned_path)
  229. VE_file_path = cr_out+'/'+filename_split[0]+'_VE_map.nii.gz'
  230. sitk.WriteImage(VE_spatial_map, VE_file_path)
  231. STD_file_path = cr_out+'/'+filename_split[0]+'_STD_map.nii.gz'
  232. sitk.WriteImage(STD_spatial_map, STD_file_path)
  233. CR_STD_file_path = cr_out+'/'+filename_split[0]+'_CR_STD_map.nii.gz'
  234. sitk.WriteImage(CR_STD_spatial_map, CR_STD_file_path)
  235. frame_mask_file = cr_out+'/'+filename_split[0]+'_frame_censoring_mask.csv'
  236. pd.DataFrame(CR_data_dict['frame_mask']).to_csv(frame_mask_file, index=False, header=['False = Masked Frames'])
  237. if cr_opts.generate_CR_null:
  238. random_CR_STD_file_path = cr_out+'/'+filename_split[0]+'_random_CR_STD_map.nii.gz'
  239. sitk.WriteImage(random_CR_STD_spatial_map, random_CR_STD_file_path)
  240. corrected_CR_STD_file_path = cr_out+'/'+filename_split[0]+'_corrected_CR_STD_map.nii.gz'
  241. sitk.WriteImage(corrected_CR_STD_spatial_map, corrected_CR_STD_file_path)
  242. else:
  243. random_CR_STD_file_path=''
  244. corrected_CR_STD_file_path=''
  245. CR_data_dict['cleaned_path'] = cleaned_path
  246. CR_data_dict['VE_file_path'] = VE_file_path
  247. CR_data_dict['STD_file_path'] = STD_file_path
  248. CR_data_dict['CR_STD_file_path'] = CR_STD_file_path
  249. CR_data_dict['random_CR_STD_file_path'] = random_CR_STD_file_path
  250. CR_data_dict['corrected_CR_STD_file_path'] = corrected_CR_STD_file_path
  251. CR_data_dict['frame_mask_file'] = frame_mask_file
  252. setattr(self, 'cleaned_path', cleaned_path)
  253. setattr(self, 'VE_file_path', VE_file_path)
  254. setattr(self, 'STD_file_path', STD_file_path)
  255. setattr(self, 'CR_STD_file_path', CR_STD_file_path)
  256. setattr(self, 'random_CR_STD_file_path', random_CR_STD_file_path)
  257. setattr(self, 'corrected_CR_STD_file_path', corrected_CR_STD_file_path)
  258. setattr(self, 'frame_mask_file', frame_mask_file)
  259. setattr(self, 'data_dict', CR_data_dict)
  260. if CR_data_dict['aroma_out'] is not None:
  261. setattr(self, 'aroma_out', CR_data_dict['aroma_out'])
  262. return runtime
  263. def _list_outputs(self):
  264. return {'cleaned_path': getattr(self, 'cleaned_path'),
  265. 'VE_file_path': getattr(self, 'VE_file_path'),
  266. 'STD_file_path': getattr(self, 'STD_file_path'),
  267. 'CR_STD_file_path': getattr(self, 'CR_STD_file_path'),
  268. 'random_CR_STD_file_path': getattr(self, 'random_CR_STD_file_path'),
  269. 'corrected_CR_STD_file_path': getattr(self, 'corrected_CR_STD_file_path'),
  270. 'frame_mask_file': getattr(self, 'frame_mask_file'),
  271. 'data_dict': getattr(self, 'data_dict'),
  272. 'aroma_out': getattr(self, 'aroma_out'),
  273. }
  274. def clean_image(input_bold, brain_mask, FD_csv, motion_params_csv, # necessary input files
  275. WM_mask=None, CSF_mask=None, vascular_mask=None,
  276. timeseries_interval='0-end', FD_censoring=False, FD_threshold=0.05,
  277. DVARS_censoring=False, MSE_censoring=False, censoring_percent_exclusion=0, TR='auto',
  278. detrending_order=1, detrending_time_interval='0-end',
  279. apply_ica_aroma=False, ica_aroma_dim=0, ica_aroma_random_seed=1,
  280. match_number_timepoints=0, highpass=None, lowpass=None, edge_cutoff=0,
  281. nuisance_regressors = [], generate_CR_null=False,
  282. scale_variance_voxelwise=False,image_scaling='grand_mean_scaling',
  283. keep_EPI_average=False,
  284. smoothing_filter=None,
  285. slicewise_correction_direction = 'Off',
  286. nipype_log=None,
  287. ):
  288. """
  289. Core function for carying confound correction of an image using the set of RABIES tools.
  290. Parameters
  291. ----------
  292. input_bold : filepath or sitk.Image
  293. The input 4D timeseries image to clean.
  294. brain_mask : filepath or sitk.Image
  295. A brain mask in the same space as input_bold
  296. FD_csv : filepath or pd.DataFrame
  297. The framewise displacement CSV generated by RABIES associated with input_bold.
  298. motion_params_csv : filepath or pd.DataFrame
  299. The motion parameters CSV generated by RABIES associated with input_bold.
  300. WM_mask : filepath or sitk.Image, default=None
  301. A WM mask in the same space as input_bold
  302. CSF_mask : filepath or sitk.Image, default=None
  303. A CSV mask in the same space as input_bold
  304. vascular_mask : filepath or sitk.Image, default=None
  305. A vascular mask in the same space as input_bold
  306. timeseries_interval : str, default='0-end'
  307. The chunk of frames to clean controlled by --timeseries_interval.
  308. FD_censoring : bool, default=False
  309. Whether to apply FD censoring.
  310. FD_threshold : float, default=0.05
  311. The FD censoring threshold.
  312. DVARS_censoring : bool, default=False
  313. Whether to apply DVARS censoring.
  314. MSE_censoring : bool, default=False
  315. Whether to apply MSE censoring.
  316. censoring_percent_exclusion : int, default=0
  317. Sets a minimum percentage of frames that must be left after censoring, otherwise returns None.
  318. TR : str, default='auto'
  319. The repetition time in seconds. If 'auto', the TR is read from the Nifti header.
  320. detrending_order : int, default=1
  321. The polynomial order for detrending.
  322. detrending_time_interval : str, default='0-end'
  323. The time interval over which the trends are computed for detrending. The trends
  324. are always removed across the entire timeseries, even if they were estimated from
  325. a subset of frames.
  326. This follows the same syntax as timeseries_interval.
  327. apply_ica_aroma : bool, default=False
  328. Whether to apply ICA-AROMA.
  329. ica_aroma_dim : int, default=0
  330. ICA dimensionality for AROMA. If 0, uses MELODIC's automated estimator.
  331. ica_aroma_random_seed : int, default=1
  332. Sets a random seed for AROMA to ensure deterministic behavior.
  333. match_number_timepoints : int, default=0
  334. Sets a fixed final number of frames for the output image. To reach
  335. this fixed number of frames, a random set of frames are additionally
  336. censored until the desired time length is reached. This can avoid
  337. inconsistent temporal degrees of freedom (tDOF) across scans as a consequence
  338. of censoring.
  339. If 0, no correction is performed.
  340. highpass : float, default=None
  341. Frequency cutoff for highpass filter.
  342. lowpass : float, default=None
  343. Frequency cutoff for lowpass filter.
  344. edge_cutoff : int, default=0
  345. Number of seconds of data to remove at each edge after frequency filtering to
  346. remove edge artefacts.
  347. nuisance_regressors : list, default=[]
  348. List of nuisance regressors. Possible options are: "WM_signal", "CSF_signal",
  349. "vascular_signal", "global_signal", "aCompCor_percent", "aCompCor_5", "mot_6",
  350. "mot_24"
  351. generate_CR_null : bool, default=False
  352. Whether to estimate overfitting from confound regression by generating phase
  353. randomised regressors, and comparing the variance explained by the real
  354. regressors VS the randomized regressors.
  355. scale_variance_voxelwise : bool, default=False
  356. If scaling was not applied voxelwise with voxelwise_standardization or
  357. voxelwise_mean, this option standardize the variance at each voxel to be equal,
  358. while preserving to total variance of the 4D timeseries (i.e. voxels have same
  359. variance, but not unit variance).
  360. image_scaling : str, default='grand_mean_scaling'
  361. Method for image scaling, select among: "None", "global_variance",
  362. "voxelwise_standardization", "grand_mean_scaling", "voxelwise_mean"
  363. keep_EPI_average : bool, default=False
  364. This option allows to re-introduce the voxelwise average that was removed during
  365. detrending at the end of confound correction, so that the original EPI background
  366. intensity is preserved.
  367. Specifically, the intercept that is calculated from the linear model during
  368. detrending is re-introduced. This intercept is also subjected to the same
  369. image_scaling as the timeseries, so as to retain the proportion between the
  370. average and variance of timeseries.
  371. Because this parameter re-introduces the intercept from detrending, if the
  372. trend was estimated over a specific time intervel with detrending_time_interval,
  373. the intercept is the average over that time interval.
  374. smoothing_filter : float, default=None
  375. Gaussian smoothing filter to apply.
  376. slicewise_correction_direction : str, default='Off'
  377. By inputing a slice direction with this parameters, the computation of nuisance
  378. signals, nuisance regression, and smoothing, are all conducted on each 2D slice
  379. independently along the provided slice direction. This strategy can be useful in
  380. the presence of artefacts (e.g. intensity spikes) that are not shared across
  381. slices due to time delays between slice acquisition, in which case a slicewise
  382. nuisance regression may perform better. Otherwise, in the case of images with
  383. spatial gaps between slices, smoothing should be applied slicewise using this
  384. parameter. To apply slicewise correction, provide as input one of the RAS axial
  385. direction for slice selection, i.e. this parameter takes as input one of: 'RL',
  386. 'AP' or 'SI'. For accurate slice selection, the axial directions of the input
  387. nifti images must be properly encoded. If no direction is provided, standard
  388. whole-brain correction is conducted.
  389. nipype_log : nipype.logging.getLogger('nipype.workflow'), default=None
  390. Provide a nipype log for printing statements.
  391. Returns
  392. -------
  393. timeseries_img : sitk.Image
  394. Output cleaned timeseries image.
  395. CR_data_dict : dict
  396. Output cleaned timeseries image.
  397. VE_spatial_map : sitk.Image
  398. R2 of nuisance regression voxelwise.
  399. STD_spatial_map : sitk.Image
  400. Voxelwise standard deviation of cleaned timeseries.
  401. CR_STD_spatial_map : sitk.Image
  402. Voxelwise standard deviation of fitted nuisance timeseries.
  403. random_CR_STD_spatial_map : sitk.Image
  404. Voxelwise standard deviation of fitted randomized regressors with
  405. generate_CR_null=True.
  406. corrected_CR_STD_spatial_map : sitk.Image
  407. Voxelwise standard deviation of fitted nuisance timeseries, after correction
  408. by the randomized regressors with generate_CR_null=True.
  409. Returns if internal error.
  410. -------
  411. None : if an error is detected in the workflow, the function only returns None.
  412. """
  413. import os
  414. import numpy as np
  415. import pandas as pd
  416. import SimpleITK as sitk
  417. from rabies.utils import recover_3D,recover_4D
  418. from . import utils as cr_utils
  419. if censoring_percent_exclusion<0 or censoring_percent_exclusion>100:
  420. raise ValueError(f"The input censoring_percent_exclusion={censoring_percent_exclusion} is not valid. It must be between 0 and 100.")
  421. '''
  422. The function can take as input either an adequately pre-loaded python object or a file path
  423. '''
  424. def read_input(input):
  425. if (input is None) or isinstance(input, sitk.Image) or isinstance(input, pd.DataFrame):
  426. return input
  427. elif os.path.isfile(input):
  428. if '.nii' in input:
  429. return sitk.ReadImage(input, sitk.sitkFloat32)
  430. elif '.csv' in input:
  431. return pd.read_csv(input)
  432. else:
  433. raise ValueError(f"The input {input} was neither a .nii or .csv file.")
  434. else:
  435. raise ValueError(f"The input {input} was neither a file nor a recognized python object.")
  436. [input_bold,
  437. FD_csv,
  438. motion_params_df,
  439. brain_mask,
  440. WM_mask,
  441. CSF_mask,
  442. vascular_mask] = [read_input(input) for input in [
  443. input_bold,
  444. FD_csv,
  445. motion_params_csv,
  446. brain_mask,
  447. WM_mask,
  448. CSF_mask,
  449. vascular_mask,
  450. ]]
  451. volume_idx = sitk.GetArrayFromImage(brain_mask).astype(bool)
  452. # save the header and original array size, since the image will be removed to space memory below
  453. from rabies.utils import get_geometry_header
  454. header_geo = get_geometry_header(input_bold)
  455. orig_4d_size = input_bold.GetSize() # copy the original array size
  456. if TR=='auto':
  457. TR = float(header_geo.GetSpacing()[3])
  458. else:
  459. TR = float(TR)
  460. # save specifically the 6 rigid parameters for AROMA
  461. motion6_regressors_array = cr_utils.select_motion_regressors(['mot_6'],motion_params_df)
  462. if len(nuisance_regressors)==0:
  463. motion_regressors_array = motion6_regressors_array
  464. else:
  465. motion_regressors_array = cr_utils.select_motion_regressors(nuisance_regressors,motion_params_df)
  466. FD_trace = FD_csv.get('MeanFD')
  467. time_range = cr_utils.prep_timeseries_interval(timeseries_interval, num_frames=orig_4d_size[3])
  468. timeseries_4d_arr = sitk.GetArrayFromImage(input_bold)
  469. timeseries_4d_arr = timeseries_4d_arr[time_range]
  470. del input_bold
  471. mse_trace = cr_utils.MSE_relative_to_average(timeseries_4d_arr)
  472. timeseries = timeseries_4d_arr[:,volume_idx] # read directly as a 2D array
  473. del timeseries_4d_arr # free memory
  474. motion_regressors_array = motion_regressors_array[time_range, :]
  475. motion6_regressors_array = motion6_regressors_array[time_range, :]
  476. FD_trace = FD_trace[time_range]
  477. '''
  478. #1 - Compute and apply frame censoring mask (from FD and/or DVARS thresholds)
  479. '''
  480. # scans are always excluded if fewer than MIN_FRAMES_POSTCENSOR frames are left, independently of censoring_percent_exclusion
  481. min_nframes_postcensor = max(cr_utils.MIN_FRAMES_POSTCENSOR, int(np.floor(timeseries.shape[0]*censoring_percent_exclusion/100)))
  482. # compute the DVARS before denoising
  483. DVARS_trace = cr_utils.get_DVARS(timeseries)
  484. frame_mask = cr_utils.temporal_censoring(FD_trace,
  485. FD_censoring, FD_threshold, DVARS_trace, DVARS_censoring,
  486. mse_trace, MSE_censoring, min_nframes_postcensor)
  487. if frame_mask is None:
  488. return None
  489. '''
  490. #2 - If --match_number_timepoints is selected, each scan is matched to the defined number of frames.
  491. '''
  492. if match_number_timepoints>0:
  493. if (highpass is not None) or (lowpass is not None):
  494. # if frequency filtering is applied, avoid selecting timepoints that would be removed with --edge_cutoff
  495. num_cut = int(edge_cutoff/TR)
  496. if not num_cut==0:
  497. frame_mask[:num_cut]=0
  498. frame_mask[-num_cut:]=0
  499. if frame_mask.sum()<match_number_timepoints:
  500. if nipype_log:
  501. nipype_log.warning(f"CONFOUND CORRECTION LEFT LESS THAN {str(match_number_timepoints)} VOLUMES. THIS SCAN WILL BE REMOVED FROM FURTHER PROCESSING.")
  502. return None
  503. # randomly shuffle indices that haven't been censored, then remove an extra subset above --match_number_timepoints
  504. num_timepoints = len(frame_mask)
  505. time_idx=np.array(range(num_timepoints))
  506. perm = np.random.permutation(time_idx[frame_mask])
  507. # selecting the subset of extra timepoints, and censoring them
  508. subset_idx = perm[match_number_timepoints:]
  509. frame_mask[subset_idx]=0
  510. # keep track of the original number of timepoints for tDOF estimation, to evaluate latter if the correction was succesful
  511. number_extra_timepoints = len(subset_idx)
  512. else:
  513. number_extra_timepoints = 0
  514. timeseries = timeseries[frame_mask]
  515. motion_regressors_array = motion_regressors_array[frame_mask]
  516. '''
  517. #3 - Linear/Quadratic detrending of fMRI timeseries and nuisance regressors
  518. '''
  519. timeseries, voxelwise_intercept = cr_utils.remove_trend(timeseries, frame_mask, order = detrending_order, time_interval = detrending_time_interval, remove_intercept=True)
  520. motion_regressors_array, _ = cr_utils.remove_trend(motion_regressors_array, frame_mask, order = detrending_order, time_interval = detrending_time_interval, remove_intercept=True)
  521. '''
  522. #4 - Apply ICA-AROMA.
  523. '''
  524. if apply_ica_aroma:
  525. # write intermediary output files for timeseries and 6 rigid body parameters
  526. inFile = os.path.abspath('aroma_input_timeseries.nii.gz')
  527. sitk.WriteImage(recover_4D(brain_mask, timeseries, header_geo), inFile)
  528. brain_mask_file = os.path.abspath("aroma_brain_mask.nii.gz")
  529. sitk.WriteImage(brain_mask, brain_mask_file)
  530. CSF_mask_file = os.path.abspath("aroma_CSF_mask.nii.gz")
  531. sitk.WriteImage(CSF_mask, CSF_mask_file)
  532. motion6_regressors_array=motion6_regressors_array[frame_mask,:]
  533. motion6_regressors_array, _ = cr_utils.remove_trend(motion6_regressors_array, frame_mask, order = detrending_order, time_interval = detrending_time_interval) # apply detrending to the confounds too
  534. df = pd.DataFrame(motion6_regressors_array)
  535. df.columns = ['mov1', 'mov2', 'mov3', 'rot1', 'rot2', 'rot3']
  536. mc_file = os.path.abspath('aroma_motion_params.csv')
  537. df.to_csv(mc_file)
  538. aroma_out = os.path.abspath('aroma_out/')
  539. cleaned_file, aroma_out = cr_utils.exec_ICA_AROMA(inFile, mc_file, brain_mask_file, CSF_mask_file, aroma_out, TR, ica_aroma_dim, random_seed=ica_aroma_random_seed)
  540. timeseries = sitk.GetArrayFromImage(sitk.ReadImage(cleaned_file, sitk.sitkFloat32))[:,volume_idx] # read directly as a 2D array
  541. else:
  542. aroma_out = None
  543. if (highpass is not None) or (lowpass is not None):
  544. '''
  545. #5 - If frequency filtering and frame censoring are applied, simulate data in censored timepoints using the Lomb-Scargle periodogram,
  546. as suggested in Power et al. (2014, Neuroimage), for both the fMRI timeseries and nuisance regressors prior to filtering.
  547. '''
  548. from rabies.confound_correction_pkg.spectral_interpolation import spectral_interpolation
  549. timeseries, _ = spectral_interpolation(x=timeseries, time_step=TR, time_mask=frame_mask, rescale=True)
  550. del _
  551. motion_regressors_array, _ = spectral_interpolation(x=motion_regressors_array, time_step=TR, time_mask=frame_mask, rescale=True)
  552. del _
  553. ### arrays are now interpolated
  554. '''
  555. #6 - As recommended in Lindquist et al. (2019, Human brain mapping), make the nuisance regressors orthogonal
  556. to the temporal filter.
  557. '''
  558. motion_regressors_array = cr_utils.butterworth(motion_regressors_array, TR=TR,
  559. high_pass=highpass, low_pass=lowpass)
  560. '''
  561. #7 - Apply highpass and/or lowpass filtering on the fMRI timeseries (with simulated timepoints).
  562. '''
  563. timeseries = cr_utils.butterworth(timeseries, TR=TR,
  564. high_pass=highpass, low_pass=lowpass)
  565. # correct for edge effects of the filters
  566. num_cut = int(edge_cutoff/TR)
  567. if len(frame_mask)<2*num_cut:
  568. raise ValueError(f"The timeseries are too short to remove {edge_cutoff}sec of data at each edge.")
  569. if not num_cut==0:
  570. frame_mask[:num_cut]=0
  571. frame_mask[-num_cut:]=0
  572. '''
  573. #8 - Re-apply the frame censoring mask onto filtered fMRI timeseries and nuisance regressors, taking out the
  574. simulated timepoints. Edge artefacts from frequency filtering can also be removed as recommended in Power et al. (2014, Neuroimage).
  575. '''
  576. # re-apply the masks to take out simulated data points, and take off the edges
  577. timeseries = timeseries[frame_mask]
  578. motion_regressors_array = motion_regressors_array[frame_mask]
  579. if frame_mask.sum()<int(min_nframes_postcensor):
  580. if nipype_log:
  581. nipype_log.warning(f"CONFOUND CORRECTION LEFT LESS THAN {str(min_nframes_postcensor)} VOLUMES. THIS SCAN WILL BE REMOVED FROM FURTHER PROCESSING.")
  582. return None
  583. '''
  584. #9 - If selected, compute the WM/CSF/vascular signal or aCompCorr and add to list of regressors. This is computed post-AROMA and filtering to
  585. minimize re-introduction of previously corrected signal fluctuations.
  586. #10 - Apply confound regression using the selected nuisance regressors.
  587. '''
  588. regress_out = cr_utils.nuisance_regression(
  589. timeseries, motion_regressors_array, TR, frame_mask, orig_4d_size, brain_mask, WM_mask, CSF_mask, vascular_mask, nuisance_regressors=nuisance_regressors,
  590. slicewise_correction_direction=slicewise_correction_direction, generate_CR_null=generate_CR_null, nipype_log=nipype_log)
  591. if regress_out is None:
  592. return None
  593. timeseries, predicted, predicted_random, num_regressors, VE_temporal, VE_spatial, VE_total_ratio, cleaned_slice_l = regress_out
  594. '''
  595. #11 - Scaling of timeseries.
  596. '''
  597. if scale_variance_voxelwise: # homogenize the variability distribution while preserving the same total variance
  598. if image_scaling=='voxelwise_standardization' or image_scaling=='voxelwise_mean':
  599. raise ValueError(f"Can't select --scale_variance_voxelwise with --image_scaling {image_scaling}.")
  600. else:
  601. temporal_std = timeseries.std(axis=0)
  602. total_std = timeseries.std()
  603. # homogenize variance across voxels
  604. timeseries /= temporal_std
  605. nan_voxels = np.isnan(timeseries).sum(axis=0)>1
  606. timeseries[:,nan_voxels] = 0
  607. scaled_total_std = timeseries.std()
  608. # rescale to preserve total variance
  609. timeseries = (timeseries/scaled_total_std)*total_std
  610. # the same scaling estimated from BOLD is applied to CR estimates to preserve accurate ratios of variance explained per voxel
  611. scaled_list = []
  612. for scaled_timeseries in [predicted,predicted_random]:
  613. scaled_timeseries /= temporal_std
  614. nan_voxels = np.isnan(scaled_timeseries).sum(axis=0)>1
  615. scaled_timeseries[:,nan_voxels] = 0
  616. scaled_timeseries = (scaled_timeseries/scaled_total_std)*total_std
  617. scaled_list.append(scaled_timeseries)
  618. del scaled_timeseries
  619. [predicted,predicted_random] = scaled_list
  620. if image_scaling=='global_variance':
  621. scaling_factor = timeseries.std()
  622. elif image_scaling=='grand_mean_scaling':
  623. scaling_factor = voxelwise_intercept.mean()/100 # we scale BOLD in % fluctuations, hence dividing by 100
  624. elif image_scaling=='voxelwise_standardization':
  625. # each voxel is scaled according to its STD
  626. scaling_factor = timeseries.std(axis=0)
  627. elif image_scaling=='voxelwise_mean':
  628. scaling_factor = voxelwise_intercept/100 # we scale BOLD in % fluctuations, hence dividing by 100
  629. else:
  630. scaling_factor = 1
  631. # scale fMRI timeseries, as well as the CR prediction to keep consistent scaling
  632. scaled_list = []
  633. for scaled_timeseries in [timeseries,predicted,predicted_random]:
  634. scaled_timeseries /= scaling_factor
  635. nan_voxels = np.isnan(scaled_timeseries).sum(axis=0)>1
  636. scaled_timeseries[:,nan_voxels] = 0
  637. scaled_list.append(scaled_timeseries)
  638. del scaled_timeseries
  639. [timeseries,predicted,predicted_random] = scaled_list
  640. # after variance scaling, compute the variability estimates
  641. temporal_std = timeseries.std(axis=0)
  642. predicted_std = predicted.std(axis=0)
  643. predicted_time = np.sqrt((predicted.T**2).mean(axis=0))
  644. predicted_global_std = predicted.std()
  645. if generate_CR_null:
  646. predicted_random_std = predicted_random.std(axis=0)
  647. # here we correct the previous STD estimates by substrating the variance explained by that of the overfitting with random regressors
  648. var_dif = predicted.var(axis=0) - predicted_random.var(axis=0)
  649. var_dif[var_dif<0] = 0 # when there's more variance explained in random regressors, set variance explained to 0
  650. corrected_predicted_std = np.sqrt(var_dif)
  651. del predicted_random
  652. del predicted
  653. # apply the frame mask to FD trace/DVARS to prepare outputs from the function
  654. DVARS_trace = DVARS_trace[frame_mask]
  655. FD_trace = FD_trace[frame_mask]
  656. mse_trace = mse_trace[frame_mask]
  657. # calculate temporal degrees of freedom left after confound correction
  658. num_timepoints = frame_mask.sum()
  659. if apply_ica_aroma:
  660. aroma_rm = (pd.read_csv(f'{aroma_out}/classification_overview.txt', sep='\t')['Motion/noise']).sum()
  661. else:
  662. aroma_rm = 0
  663. tDOF = num_timepoints - (aroma_rm+num_regressors) + number_extra_timepoints
  664. # compute DVARS correlation with FD post-correction
  665. DVARS_cleaned_trace = cr_utils.get_DVARS(timeseries)
  666. FD_DVARS_corr = np.corrcoef(DVARS_cleaned_trace[1:], FD_trace[1:])[0,1] # skip first index which is 0 for DVARS
  667. # smoothing takes an SITK image
  668. timeseries_img = recover_4D(brain_mask, timeseries, header_geo)
  669. del timeseries
  670. if smoothing_filter is not None:
  671. '''
  672. #12 - Apply Gaussian spatial smoothing.
  673. '''
  674. if not slicewise_correction_direction=='Off':
  675. # smooth 1 slice at a time
  676. for slice in cleaned_slice_l:
  677. # here the axis direction is in proper order since we are indexing a SITK image
  678. if slicewise_correction_direction=='RL':
  679. timeseries_img[slice:slice+1,:,:,:] = cr_utils.smooth_image(
  680. timeseries_img[slice:slice+1,:,:,:], smoothing_filter, brain_mask[slice:slice+1,:,:])
  681. elif slicewise_correction_direction=='AP':
  682. timeseries_img[:,slice:slice+1,:,:] = cr_utils.smooth_image(
  683. timeseries_img[:,slice:slice+1,:,:], smoothing_filter, brain_mask[:,slice:slice+1,:])
  684. elif slicewise_correction_direction=='SI':
  685. timeseries_img[:,:,slice:slice+1,:] = cr_utils.smooth_image(
  686. timeseries_img[:,:,slice:slice+1,:], smoothing_filter, brain_mask[:,:,slice:slice+1])
  687. else:
  688. raise ValueError(f"slicewise_correction_direction must be one of 'RL', 'AP' or 'SI', got {slicewise_correction_direction} instead.")
  689. else:
  690. timeseries_img = cr_utils.smooth_image(
  691. timeseries_img, smoothing_filter, brain_mask)
  692. voxelwise_intercept /= scaling_factor # the intercept is also scaling, so that the mean and variance remain proportional to the original data
  693. if keep_EPI_average:
  694. # this needs to be done after smoothing, because the intercept would impact smoothing results
  695. timeseries = sitk.GetArrayFromImage(timeseries_img)[:,volume_idx]
  696. del timeseries_img
  697. timeseries += voxelwise_intercept
  698. timeseries_img = recover_4D(brain_mask, timeseries, header_geo)
  699. # save output files
  700. VE_spatial_map = recover_3D(brain_mask, VE_spatial)
  701. STD_spatial_map = recover_3D(brain_mask, temporal_std)
  702. CR_STD_spatial_map = recover_3D(brain_mask, predicted_std)
  703. del VE_spatial, temporal_std, predicted_std
  704. if generate_CR_null:
  705. random_CR_STD_spatial_map = recover_3D(brain_mask, predicted_random_std)
  706. corrected_CR_STD_spatial_map = recover_3D(brain_mask, corrected_predicted_std)
  707. del predicted_random_std, corrected_predicted_std
  708. else:
  709. random_CR_STD_spatial_map = None
  710. corrected_CR_STD_spatial_map = None
  711. CR_data_dict = {
  712. 'TR':TR, 'FD_trace':FD_trace, 'DVARS_pre_correction':DVARS_trace, 'DVARS_post_correction':DVARS_cleaned_trace, 'FD_DVARS_corr':FD_DVARS_corr, 'mse_trace':mse_trace, 'time_range':time_range, 'frame_mask':frame_mask, 'VE_temporal':VE_temporal,
  713. 'motion_params_df':motion_params_df, 'predicted_time':predicted_time, 'tDOF':tDOF, 'CR_global_std':predicted_global_std,
  714. 'VE_total_ratio':VE_total_ratio, 'voxelwise_mean':voxelwise_intercept, 'aroma_out':aroma_out,
  715. }
  716. return timeseries_img, CR_data_dict, VE_spatial_map, STD_spatial_map, CR_STD_spatial_map, random_CR_STD_spatial_map, corrected_CR_STD_spatial_map

confound_correction.py at commit 21ec843, under other · at the source

Overview

Authors: Ezra Winter-Nelson1,2, Eyal Bergmann3,4, Micaela Y Chan1, Gabriella Vill4, Liang Han1, Ziwei Zhang1, Alexandra Kavushansky3, Irit Dolgopyat3, Jad Asleh3, Jennifer D Whitesell5, Itamar Kahn4, Gagan S Wig1,2,6
  1. Center for Vital Longevity, The University of Texas at Dallas, Dallas, TX 75235
  2. Department of Psychology, School of Behavioral and Brain Sciences, The University of Texas at Dallas, Dallas, TX 75080
  3. Department of Neuroscience, Rappaport Faculty of Medicine, Technion—Israel Institute of Technology, Haifa, Israel 3525433
  4. Zuckerman Mind Brain Behavior Institute and Department of Neuroscience, Columbia University, New York, NY
  5. Allen Institute for Brain Science, Seattle, WA 98109
  6. Department of Psychiatry, The University of Texas Southwestern Medical Center, Dallas, TX 75390
Dates: received 7 October 2025; accepted 20 February 2026; published online 27 March 2026; in print 31 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1073/pnas.2527522123 · PMID 41894329 · PMCID PMC13037878 · OpenAlex W7141336682
Open access: hybrid, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: fMRI (modality), human (organism), mouse (organism)
Methods: Spectral & time-frequency, Connectivity, Statistics, fMRI & imaging
Keywords: brain networks, system segregation, aging, resting-state correlations, mouse
MeSH: Aging*, Brain*, Connectome*, Magnetic Resonance Imaging*, Adolescent, Adult, Aged, Aged, 80 and over, Animals, Female, Humans, Male, Mice, Middle Aged, Young Adult (* major topic)
Topic: Functional Brain Connectivity Studies (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: HHS | NIH | National Institute on Aging (NIA) (AG092219, AG063930, R24AG065172); James S. McDonnell Foundation (JSMF) (Understanding Human Cognition Scholar Award); NIA NIH HHS (R01 AG092219)
Citations: cited by 5 papers (Europe PMC); 125 references in the paper

Abstract

Human aging is marked by progressive reorganization of large-scale functional brain networks; these brain network changes have been linked to cognitive decline and disease vulnerability. Conversely, while mice have served as powerful models for understanding the molecular and cellular changes that occur over the lifespan, an absence of precise characterization of age-related changes in large-scale functional brain network organization has limited cross-species translational insights. Here, using densely sampled resting-state functional MRI data acquired cross-sectionally and longitudinally in awake mice over a broad range of adulthood (n = 82; 3 to 20 mo), we describe organizational features and age-related alterations of the mouse’s functional connectome. Mouse resting-state functional connectivity recapitulates known functional circuits, demonstrating the organizational validity of these signals. Graph theoretic analysis applied to functional connectivity reveals that mice exhibit modular architectures of functional brain network organization and that increasing age is associated with decreasing system segregation, indicative of network dedifferentiation analogous to observations in humans. Notably, mouse resting-state brain networks are more segregated than those of humans [determined using data from the Human Connectome Project and its developmental- and aging-counterparts (n = 1,179; 18 to 90 y)], attributable to mice exhibiting a diminished contribution of long-range functional relationships that integrate distributed systems. Mice also exhibit slower rates of age-related decline in brain network organization relative to humans, highlighting important species differences in functional brain network organization and trajectories of brain network aging. These findings establish a model of large-scale functional brain network aging in mice and provide a translational bridge across species and spatial scales of analysis.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

wiglab/mouse-community-nodes

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: eec8ea9ccf0e0494b2c84e0acd2a57e8233e49a4, 9 January 2026
Size: 316 files
Software Heritage: not archived
Found in: the references
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
1 file

wiglab/system-segregation-and-graph-tools

License: MIT
State: the link answers, verified on 1 November 2026
Evidence: files inventoried
Commit: e7c30345b8ebf29e3a9ab44593d95e41cd0b16ed, 15 October 2024
Languages: MATLAB (11), R (6)
Size: 22 files, 17 scripts
Software Heritage: not archived
Found in: “Data, Materials, and Software Availability”
Holds: README, license file, environment (DESCRIPTION), tests, continuous integration
Not found: CITATION.cff, documentation
Tools: GIfTI library for MATLAB (3 files), cifti-matlab (2 files)
Availability: 2 checks, the latest on 1 November 2026: the link answers
  • 1 November 2026: the link answers
  • 30 September 2026: the link answers
19 files

SteenMoeller/NORDIC_Raw

License: other
State: the link answers, verified on 30 October 2026
Evidence: files inventoried
Commit: 58c20de2482d773e3a9b852dd9538a1974351b84, 30 September 2026
Languages: MATLAB (5)
Size: 8 files, 5 scripts
Software Heritage: not archived
Found in: “Data, Materials, and Software Availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 2 checks, the latest on 30 October 2026: the link answers
  • 30 October 2026: the link answers
  • 30 September 2026: the link answers
7 files

CoBrALab/RABIES

License: other
State: the link answers, verified on 30 October 2026
Evidence: files inventoried
Commit: 21ec8439434c177a6ef61dcd1f1e2a2d42f825f4, 27 October 2026
Languages: Python (47), Shell (5)
Size: 139 files, 52 scripts
Software Heritage: archived
Found in: “Data, Materials, and Software Availability”
Holds: README, license file, CITATION.cff, environment (Dockerfile, setup.py, docs/requirements.txt, rabies/confound_correction_pkg/mod_ICA_AROMA/Dockerfile), continuous integration, documentation
Not found: tests
Tools: SimpleITK (23 files), Nipype (22 files), NumPy (21 files), pandas (13 files), Matplotlib (9 files), ANTs (4 files), SciPy (4 files), FSL (3 files), PyBIDS (3 files), NiBabel (2 files), scikit-learn (2 files), scikit-image (1 file), seaborn (1 file)
Availability: 2 checks, the latest on 30 October 2026: the link answers
  • 30 October 2026: the link answers
  • 27 September 2026: the link answers
54 files

Washington-University/HCPpipelines

License: BSD-2-Clause
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: fb25ef4e9be44402d1d746834d18050842bb4042, 24 September 2026
Languages: MATLAB (223), Shell (222), Python (13), C (3)
Size: 948 files, 461 scripts
Software Heritage: not archived
Found in: “Data, Materials, and Software Availability”
Holds: README, license file, environment (environment.yml), documentation
Not found: CITATION.cff, tests, continuous integration
Tools: FSL (135 files), HCP Pipelines (120 files), Connectome Workbench (80 files), cifti-matlab (31 files), FreeSurfer (17 files), NumPy (12 files), Statistics and Machine Learning Toolbox (7 files), Signal Processing Toolbox (6 files), NiBabel (4 files), Image Processing Toolbox (3 files), scikit-learn (3 files), XGBoost (3 files), GIfTI library for MATLAB (2 files), pandas (2 files), ANTs (1 file), dcm2niix (1 file), Curve Fitting Toolbox (1 file), Parallel Computing Toolbox (1 file), pydicom (1 file), SciPy (1 file), SPM (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
463 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 5 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 535 scripts, each with its path and the digest of its content;
  • 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data, Materials, and Software Availability

The human data used in this study are publicly available. The HCP-D dataset can be downloaded from: https://www.humanconnectome.org/study/hcp-lifespan-development/data-releases (58), the HCP-YA dataset can be downloaded from: https://www.humanconnectome.org/study/hcp-young-adult/data-releases (107), and the HCP-A dataset can be downloaded from https://www.humanconnectome.org/study/hcp-lifespan-aging/data-releases (56). Mouse data used in the study are publicly available in BIDS format at https://openneuro.org/datasets/ds007100 (122). Functional system labels and adjusted ROIs used in the primary and supplementary analyses, respectively, are available at https://gitlab.com/wiglab/mouse-community-nodes (123). Code for system segregation calculation is available at: https://gitlab.com/wiglab/system-segregation-and-graph-tools (124). Code for NORDIC used in mouse preprocessing is available at: https://github.com/SteenMoeller/NORDIC_Raw (112). Code for RABIES used in mouse preprocessing is available at: https://github.com/CoBrALab/RABIES (114). Code for the HCP-fMRI preprocessing pipeline is available at: https://github.com/Washington-University/HCPpipelines (125).

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 12 authors, 5 keywords, 15 MeSH terms, 3 funders, 120 references.

Cite

This paper

Winter-Nelson, E., Bergmann, E., Chan, M. Y., Vill, G., Han, L., Zhang, Z., Kavushansky, A., Dolgopyat, I., Asleh, J., Whitesell, J. D., Kahn, I., & Wig, G. S. (2026). Correspondence of large-scale functional brain network decline across aging mice and humans. Proceedings of the National Academy of Sciences of the United States of America, 123(13), e2527522123. https://doi.org/10.1073/pnas.2527522123

BibTeX

@article{winternelson2026correspondence,
author = {Winter-Nelson, Ezra and Bergmann, Eyal and Chan, Micaela Y and Vill, Gabriella and Han, Liang and Zhang, Ziwei and Kavushansky, Alexandra and Dolgopyat, Irit and Asleh, Jad and Whitesell, Jennifer D and Kahn, Itamar and Wig, Gagan S},
title = {{Correspondence of large-scale functional brain network decline across aging mice and humans}},
journal = {Proceedings of the National Academy of Sciences of the United States of America},
year = {2026},
month = mar,
volume = {123},
number = {13},
pages = {e2527522123},
publisher = {National Academy of Sciences},
issn = {0027-8424},
doi = {10.1073/pnas.2527522123},
url = {https://doi.org/10.1073/pnas.2527522123},
pmid = {41894329},
pmcid = {PMC13037878}
}

RIS

TY - JOUR
AU - Winter-Nelson, Ezra
AU - Bergmann, Eyal
AU - Chan, Micaela Y
AU - Vill, Gabriella
AU - Han, Liang
AU - Zhang, Ziwei
AU - Kavushansky, Alexandra
AU - Dolgopyat, Irit
AU - Asleh, Jad
AU - Whitesell, Jennifer D
AU - Kahn, Itamar
AU - Wig, Gagan S
TI - Correspondence of large-scale functional brain network decline across aging mice and humans
T2 - Proceedings of the National Academy of Sciences of the United States of America
J2 - Proc Natl Acad Sci U S A
PY - 2026
DA - 2026/03/27
VL - 123
IS - 13
SP - e2527522123
SN - 0027-8424
PB - National Academy of Sciences
DO - 10.1073/pnas.2527522123
UR - https://doi.org/10.1073/pnas.2527522123
LA - en
ER -

CSL-JSON

{
"id": "10.1073/pnas.2527522123",
"type": "article-journal",
"title": "Correspondence of large-scale functional brain network decline across aging mice and humans",
"container-title": "Proceedings of the National Academy of Sciences of the United States of America",
"author": [
{
"family": "Winter-Nelson",
"given": "Ezra"
},
{
"family": "Bergmann",
"given": "Eyal"
},
{
"family": "Chan",
"given": "Micaela Y"
},
{
"family": "Vill",
"given": "Gabriella"
},
{
"family": "Han",
"given": "Liang"
},
{
"family": "Zhang",
"given": "Ziwei"
},
{
"family": "Kavushansky",
"given": "Alexandra"
},
{
"family": "Dolgopyat",
"given": "Irit"
},
{
"family": "Asleh",
"given": "Jad"
},
{
"family": "Whitesell",
"given": "Jennifer D"
},
{
"family": "Kahn",
"given": "Itamar"
},
{
"family": "Wig",
"given": "Gagan S"
}
],
"container-title-short": "Proc Natl Acad Sci U S A",
"volume": "123",
"issue": "13",
"page": "e2527522123",
"DOI": "10.1073/pnas.2527522123",
"PMID": "41894329",
"PMCID": "PMC13037878",
"ISSN": "0027-8424",
"publisher": "National Academy of Sciences",
"URL": "https://doi.org/10.1073/pnas.2527522123",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
27
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1162/imag.a.1338 [code]
Systematic fMRI signal differences across cohorts alter lifespan trajectories of functional brain networks.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: cifti-matlab, GIfTI library for MATLAB, scikit-learn, 2 other tools, humanconnectome.org/study/hcp-lifespan-aging, humanconnectome.org/study/hcp-young-adult, fMRI, 14 references, 2 authors
[2] doi:10.1038/s41467-025-62812-9 [code]
Mapping the coupling between tract reachability and cortical geometry of the human brain.
Journal: Nature communications
In common: HCP Pipelines, dcm2niix, cifti-matlab, 21 other tools, humanconnectome.org/study/hcp-lifespan-development, 4 references
[3] doi:10.1038/s42003-025-08509-7 [code]
Development of areal-level individualized homologous functional parcellations in youth.
Journal: Communications biology
In common: HCP Pipelines, dcm2niix, cifti-matlab, 20 other tools, fMRI, 8 references
[4] doi:10.1038/s41593-024-01596-5 [code]
A precision functional atlas of personalized network topography and probabilities.
Journal: Nature neuroscience
In common: HCP Pipelines, cifti-matlab, PyBIDS, 9 other tools, humanconnectome.org/study/hcp-lifespan-development, 11 references
[5] doi:10.1371/journal.pbio.3003856 [code]
Aging and metabolism contribute separately to brain-body health.
Journal: PLoS biology
In common: HCP Pipelines, Connectome Workbench, GIfTI library for MATLAB, 14 other tools, humanconnectome.org/study/hcp-lifespan-aging, 8 references
[6] doi:10.1093/cercor/bhae278 [code]
Task and stimulus coding in the multiple-demand network.
Journal: Cerebral cortex (New York, N.Y. : 1991)
In common: HCP Pipelines, dcm2niix, cifti-matlab, 18 other tools, 1 reference
[7] doi:10.34133/research.1143 [code]
Lifestyle and Transcriptional Signatures Associated with Ethnicity/Race-Related Variations in the Functional Connectome.
Journal: Research (Washington, D.C.)
In common: Connectome Workbench, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 12 other tools, humanconnectome.org/study/hcp-lifespan-development, humanconnectome.org/study/hcp-young-adult, 6 references
[8] doi:10.1523/jneurosci.0579-23.2023 [code]
Dissociable Effects of Alzheimer's Disease-Related Cognitive Dysfunction and Aging on Functional Brain Network Segregation.
Journal: The Journal of neuroscience : the official journal of the Society for Neuroscience
In common: cifti-matlab, GIfTI library for MATLAB, seaborn, 2 other tools, fMRI, 10 references, 2 authors
[9] doi:10.1038/s41467-023-44363-z [code]
Multimodal measures of spontaneous brain activity reveal both common and divergent patterns of cortical functional organization.
Journal: Nature communications
In common: PyBIDS, Nipype, SimpleITK, 10 other tools, fMRI, mouse, 10 references
[10] doi:10.1038/s41586-026-10631-3 [code]
A prognostic human brain network for diffuse midline glioma.
Journal: Nature
In common: cifti-matlab, pydicom, Curve Fitting Toolbox, 17 other tools, 5 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1073/pnas.2527522123.svg)](https://openscicode.org/badge/doi/10.1073/pnas.2527522123)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1073/pnas.2527522123.svg
   :target: https://openscicode.org/badge/doi/10.1073/pnas.2527522123
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1073/pnas.2527522123"><img src="https://openscicode.org/badge/doi/10.1073/pnas.2527522123.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.