junifer 0.0.7.dev332__py3-none-any.whl → 0.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. junifer/_version.py +9 -19
  2. junifer/api/__init__.pyi +17 -2
  3. junifer/api/functions.py +222 -39
  4. junifer/api/queue_context/__init__.pyi +11 -3
  5. junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
  6. junifer/api/queue_context/htcondor_adapter.py +118 -181
  7. junifer/api/queue_context/queue_context_adapter.py +51 -7
  8. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
  9. junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
  10. junifer/api/res/fsl/run_fsl_docker.sh +4 -0
  11. junifer/api/tests/test_functions.py +863 -36
  12. junifer/cli/__init__.py +1 -7
  13. junifer/cli/cli.py +41 -22
  14. junifer/cli/parser.py +23 -147
  15. junifer/cli/tests/data/gmd_mean.yaml +6 -6
  16. junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
  17. junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
  18. junifer/cli/tests/test_cli.py +120 -4
  19. junifer/cli/tests/test_cli_utils.py +23 -6
  20. junifer/cli/utils.py +14 -9
  21. junifer/configs/__init__.py +5 -0
  22. junifer/configs/__init__.pyi +0 -0
  23. junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
  24. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
  25. junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
  26. junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
  27. junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
  28. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
  29. junifer/configs/juseless/datagrabbers/ucla.py +111 -104
  30. junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
  31. junifer/configs/py.typed +0 -0
  32. junifer/conftest.py +63 -24
  33. junifer/data/_dispatch.py +9 -19
  34. junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
  35. junifer/data/coordinates/_coordinates.py +51 -6
  36. junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
  37. junifer/data/coordinates/tests/test_coordinates.py +15 -3
  38. junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
  39. junifer/data/maps/_ants_maps_warper.py +18 -12
  40. junifer/data/maps/_fsl_maps_warper.py +7 -3
  41. junifer/data/maps/_maps.py +12 -8
  42. junifer/data/maps/tests/test_maps.py +13 -21
  43. junifer/data/masks/_ants_mask_warper.py +18 -12
  44. junifer/data/masks/_fsl_mask_warper.py +7 -3
  45. junifer/data/masks/_masks.py +15 -12
  46. junifer/data/masks/tests/test_masks.py +20 -17
  47. junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
  48. junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
  49. junifer/data/parcellations/_parcellations.py +125 -91
  50. junifer/data/parcellations/tests/test_parcellations.py +122 -1
  51. junifer/data/template_spaces.py +117 -50
  52. junifer/data/tests/test_template_spaces.py +10 -7
  53. junifer/data/utils.py +10 -8
  54. junifer/datagrabber/__init__.pyi +38 -5
  55. junifer/datagrabber/aomic/__init__.pyi +8 -1
  56. junifer/datagrabber/aomic/_types.py +22 -0
  57. junifer/datagrabber/aomic/id1000.py +193 -183
  58. junifer/datagrabber/aomic/piop1.py +215 -259
  59. junifer/datagrabber/aomic/piop2.py +211 -244
  60. junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
  61. junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
  62. junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
  63. junifer/datagrabber/base.py +253 -79
  64. junifer/datagrabber/datalad_base.py +127 -76
  65. junifer/datagrabber/dmcc13_benchmark.py +204 -289
  66. junifer/datagrabber/hcp1200/__init__.pyi +7 -2
  67. junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
  68. junifer/datagrabber/hcp1200/hcp1200.py +151 -164
  69. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
  70. junifer/datagrabber/multiple.py +183 -63
  71. junifer/datagrabber/pattern.py +358 -227
  72. junifer/datagrabber/pattern_datalad.py +84 -124
  73. junifer/datagrabber/pattern_validation_mixin.py +22 -50
  74. junifer/datagrabber/tests/test_base.py +17 -4
  75. junifer/datagrabber/tests/test_datalad_base.py +20 -27
  76. junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
  77. junifer/datagrabber/tests/test_multiple.py +293 -31
  78. junifer/datagrabber/tests/test_pattern.py +577 -18
  79. junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
  80. junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
  81. junifer/datareader/default.py +8 -5
  82. junifer/datareader/tests/test_default_reader.py +1 -1
  83. junifer/external/BrainPrint/.codecov.yml +2 -0
  84. junifer/external/BrainPrint/.codespellignore +2 -0
  85. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
  86. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
  87. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
  88. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
  89. junifer/external/BrainPrint/.github/dependabot.yml +6 -0
  90. junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
  91. junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
  92. junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
  93. junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
  94. junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
  95. junifer/external/BrainPrint/.gitignore +123 -0
  96. junifer/external/BrainPrint/LICENSE +21 -0
  97. junifer/external/BrainPrint/README.md +108 -0
  98. junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
  99. junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
  100. junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
  101. junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
  102. junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
  103. junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
  104. junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
  105. junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
  106. junifer/external/BrainPrint/doc/Makefile +20 -0
  107. junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
  108. junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
  109. junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
  110. junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
  111. junifer/external/BrainPrint/doc/api/index.rst +11 -0
  112. junifer/external/BrainPrint/doc/conf.py +272 -0
  113. junifer/external/BrainPrint/doc/index.rst +140 -0
  114. junifer/external/BrainPrint/doc/links.inc +36 -0
  115. junifer/external/BrainPrint/doc/make.bat +35 -0
  116. junifer/external/BrainPrint/doc/references.bib +23 -0
  117. junifer/external/BrainPrint/pyproject.toml +170 -0
  118. junifer/external/BrainPrint/setup.py +3 -0
  119. junifer/external/__init__.py +5 -0
  120. junifer/external/__init__.pyi +0 -0
  121. junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
  122. junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
  123. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
  124. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
  125. junifer/external/py.typed +0 -0
  126. junifer/markers/__init__.pyi +6 -3
  127. junifer/markers/base.py +126 -76
  128. junifer/markers/brainprint.py +30 -39
  129. junifer/markers/complexity/__init__.py +20 -3
  130. junifer/markers/complexity/complexity_base.py +40 -41
  131. junifer/markers/complexity/hurst_exponent.py +22 -35
  132. junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
  133. junifer/markers/complexity/perm_entropy.py +22 -36
  134. junifer/markers/complexity/range_entropy.py +22 -35
  135. junifer/markers/complexity/range_entropy_auc.py +27 -36
  136. junifer/markers/complexity/sample_entropy.py +22 -36
  137. junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
  138. junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
  139. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
  140. junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
  141. junifer/markers/complexity/tests/test_range_entropy.py +47 -36
  142. junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
  143. junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
  144. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
  145. junifer/markers/complexity/weighted_perm_entropy.py +22 -36
  146. junifer/markers/ets_rss.py +35 -31
  147. junifer/markers/falff/__init__.pyi +7 -1
  148. junifer/markers/falff/_afni_falff.py +22 -9
  149. junifer/markers/falff/_junifer_falff.py +47 -31
  150. junifer/markers/falff/falff_base.py +80 -60
  151. junifer/markers/falff/falff_maps.py +17 -41
  152. junifer/markers/falff/falff_parcels.py +26 -51
  153. junifer/markers/falff/falff_spheres.py +34 -61
  154. junifer/markers/falff/tests/test_falff_maps.py +20 -23
  155. junifer/markers/falff/tests/test_falff_mask.py +99 -0
  156. junifer/markers/falff/tests/test_falff_parcels.py +14 -10
  157. junifer/markers/falff/tests/test_falff_spheres.py +14 -10
  158. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
  159. junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
  160. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
  161. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
  162. junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
  163. junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
  164. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
  165. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
  166. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
  167. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
  168. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
  169. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
  170. junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
  171. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
  172. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
  173. junifer/markers/maps_aggregation.py +77 -55
  174. junifer/markers/parcel_aggregation.py +91 -70
  175. junifer/markers/reho/__init__.pyi +7 -1
  176. junifer/markers/reho/_afni_reho.py +23 -14
  177. junifer/markers/reho/_junifer_reho.py +112 -123
  178. junifer/markers/reho/reho_base.py +115 -37
  179. junifer/markers/reho/reho_maps.py +18 -32
  180. junifer/markers/reho/reho_parcels.py +26 -41
  181. junifer/markers/reho/reho_spheres.py +33 -54
  182. junifer/markers/reho/tests/test_junifer_reho.py +178 -0
  183. junifer/markers/reho/tests/test_reho_maps.py +27 -25
  184. junifer/markers/reho/tests/test_reho_parcels.py +28 -22
  185. junifer/markers/reho/tests/test_reho_spheres.py +16 -12
  186. junifer/markers/sphere_aggregation.py +88 -72
  187. junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
  188. junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
  189. junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
  190. junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
  191. junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
  192. junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
  193. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
  194. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
  195. junifer/markers/tests/test_brainprint.py +10 -9
  196. junifer/markers/tests/test_ets_rss.py +7 -3
  197. junifer/markers/tests/test_lost_regions.py +279 -0
  198. junifer/markers/tests/test_maps_aggregation.py +67 -114
  199. junifer/markers/tests/test_marker_utils.py +40 -1
  200. junifer/markers/tests/test_markers_base.py +275 -6
  201. junifer/markers/tests/test_parcel_aggregation.py +91 -53
  202. junifer/markers/tests/test_sphere_aggregation.py +53 -47
  203. junifer/markers/utils.py +267 -5
  204. junifer/onthefly/__init__.py +2 -3
  205. junifer/onthefly/__init__.pyi +4 -0
  206. junifer/onthefly/_brainprint.py +3 -5
  207. junifer/onthefly/py.typed +0 -0
  208. junifer/onthefly/read_transform.py +11 -9
  209. junifer/onthefly/tests/test_read_transform.py +7 -7
  210. junifer/pipeline/__init__.pyi +4 -2
  211. junifer/pipeline/marker_collection.py +14 -10
  212. junifer/pipeline/pipeline_component_registry.py +25 -6
  213. junifer/pipeline/pipeline_step_mixin.py +5 -12
  214. junifer/pipeline/tests/test_data_object_dumper.py +10 -10
  215. junifer/pipeline/tests/test_marker_collection.py +145 -168
  216. junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
  217. junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
  218. junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
  219. junifer/pipeline/tests/test_workdir_manager.py +39 -0
  220. junifer/pipeline/update_meta_mixin.py +13 -10
  221. junifer/pipeline/utils.py +22 -17
  222. junifer/pipeline/workdir_manager.py +41 -32
  223. junifer/preprocess/__init__.pyi +9 -4
  224. junifer/preprocess/_temporal_filter.py +34 -33
  225. junifer/preprocess/_temporal_slicer.py +17 -30
  226. junifer/preprocess/base.py +99 -55
  227. junifer/preprocess/confounds/__init__.pyi +10 -2
  228. junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
  229. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
  230. junifer/preprocess/smoothing/__init__.pyi +2 -2
  231. junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
  232. junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
  233. junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
  234. junifer/preprocess/smoothing/smoothing.py +59 -40
  235. junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
  236. junifer/preprocess/tests/test_preprocess_base.py +17 -9
  237. junifer/preprocess/tests/test_temporal_filter.py +37 -16
  238. junifer/preprocess/tests/test_temporal_slicer.py +12 -12
  239. junifer/preprocess/warping/__init__.pyi +2 -2
  240. junifer/preprocess/warping/_ants_warper.py +56 -40
  241. junifer/preprocess/warping/_fsl_warper.py +49 -36
  242. junifer/preprocess/warping/_utils.py +106 -0
  243. junifer/preprocess/warping/space_warper.py +99 -98
  244. junifer/preprocess/warping/tests/test_space_warper.py +46 -41
  245. junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
  246. junifer/stats.py +46 -34
  247. junifer/storage/__init__.pyi +8 -4
  248. junifer/storage/base.py +129 -64
  249. junifer/storage/hdf5.py +307 -199
  250. junifer/storage/pandas_base.py +46 -24
  251. junifer/storage/sqlite.py +99 -62
  252. junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
  253. junifer/storage/tests/test_hdf5.py +147 -12
  254. junifer/storage/tests/test_repeated_storage.py +293 -0
  255. junifer/storage/tests/test_sqlite.py +9 -20
  256. junifer/storage/tests/test_storage_base.py +18 -21
  257. junifer/storage/tests/test_utils.py +83 -22
  258. junifer/storage/utils.py +58 -38
  259. junifer/testing/__init__.pyi +2 -2
  260. junifer/testing/datagrabbers.py +480 -44
  261. junifer/testing/registry.py +12 -0
  262. junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
  263. junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
  264. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
  265. junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
  266. junifer/testing/tests/test_testing_registry.py +31 -0
  267. junifer/testing/tests/test_testing_utils.py +35 -0
  268. junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
  269. junifer/testing/utils.py +33 -1
  270. junifer/tests/test_stats.py +1 -5
  271. junifer/typing/__init__.pyi +2 -0
  272. junifer/typing/_typing.py +34 -19
  273. junifer/typing/py.typed +0 -0
  274. junifer/utils/__init__.pyi +10 -1
  275. junifer/utils/_config.py +1 -2
  276. junifer/utils/helpers.py +68 -2
  277. junifer/utils/logging.py +31 -7
  278. junifer/utils/tests/test_helpers.py +36 -1
  279. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
  280. junifer-0.0.8.dist-info/RECORD +402 -0
  281. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
  282. junifer/cli/tests/test_parser.py +0 -280
  283. junifer/external/h5io/h5io/__init__.py +0 -15
  284. junifer/external/h5io/h5io/_h5io.py +0 -767
  285. junifer/external/h5io/h5io/_version.py +0 -1
  286. junifer/external/h5io/h5io/chunked_array.py +0 -94
  287. junifer/external/h5io/h5io/chunked_list.py +0 -65
  288. junifer/markers/complexity/__init__.pyi +0 -17
  289. junifer-0.0.7.dev332.dist-info/RECORD +0 -353
  290. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
  291. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
  292. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
  293. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
@@ -4,15 +4,52 @@
4
4
  # Leonard Sasse <l.sasse@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
+ from enum import StrEnum
7
8
  from pathlib import Path
8
- from typing import Union
9
+ from typing import Annotated, Literal
10
+
11
+ from pydantic import BeforeValidator
9
12
 
10
13
  from ....api.decorators import register_datagrabber
11
- from ....datagrabber import PatternDataGrabber
12
- from ....utils import raise_error
14
+ from ....datagrabber import ConfoundsFormat, DataType, PatternDataGrabber
15
+ from ....typing import DataGrabberPatterns
16
+ from ....utils import ensure_list
17
+
18
+
19
+ __all__ = ["JuselessUCLA", "UCLATask"]
20
+
21
+
22
+ class UCLATask(StrEnum):
23
+ """Accepted UCLA tasks."""
13
24
 
25
+ REST = "rest"
26
+ BART = "bart"
27
+ BHT = "bht"
28
+ PAMENC = "pamenc"
29
+ PAMRET = "pamret"
30
+ SCAP = "scap"
31
+ TASKSWITCH = "taskswitch"
32
+ STOPSIGNAL = "stopsignal"
14
33
 
15
- __all__ = ["JuselessUCLA"]
34
+
35
+ _types = Literal[
36
+ DataType.BOLD,
37
+ DataType.T1w,
38
+ DataType.VBM_CSF,
39
+ DataType.VBM_GM,
40
+ DataType.VBM_WM,
41
+ ]
42
+
43
+ _tasks = Literal[
44
+ UCLATask.REST,
45
+ UCLATask.BART,
46
+ UCLATask.BHT,
47
+ UCLATask.PAMENC,
48
+ UCLATask.PAMRET,
49
+ UCLATask.SCAP,
50
+ UCLATask.TASKSWITCH,
51
+ UCLATask.STOPSIGNAL,
52
+ ]
16
53
 
17
54
 
18
55
  @register_datagrabber
@@ -23,118 +60,88 @@ class JuselessUCLA(PatternDataGrabber):
23
60
 
24
61
  Parameters
25
62
  ----------
26
- datadir : str or Path, optional
63
+ datadir : Path, optional
27
64
  The directory where the dataset is stored.
28
65
  (default "/data/project/psychosis_thalamus/data/fmriprep").
29
66
  types: {"BOLD", "T1w", "VBM_CSF", "VBM_GM", "VBM_WM"} or \
30
67
  list of the options, optional
31
- UCLA data types. If None, all available data types are selected.
32
- (default None).
68
+ The data type(s) to grab.
33
69
  tasks : {"rest", "bart", "bht", "pamenc", "pamret", \
34
70
  "scap", "taskswitch", "stopsignal"} or \
35
- list of the options or None, optional
36
- UCLA task sessions. If None, all available task sessions are
37
- selected (default None).
71
+ list of the options, optional
72
+ UCLA task sessions.
73
+ By default, all available task are selected.
38
74
 
39
75
  """
40
76
 
41
- def __init__(
42
- self,
43
- datadir: Union[
44
- str, Path
45
- ] = "/data/project/psychosis_thalamus/data/fmriprep",
46
- types: Union[str, list[str], None] = None,
47
- tasks: Union[str, list[str], None] = None,
48
- ) -> None:
49
- # Declare all tasks
50
- all_tasks = [
51
- "rest",
52
- "bart",
53
- "bht",
54
- "pamenc",
55
- "pamret",
56
- "scap",
57
- "taskswitch",
58
- "stopsignal",
59
- ]
60
- # Set default tasks
61
- if tasks is None:
62
- tasks = all_tasks
63
- else:
64
- # Convert single task into list
65
- if isinstance(tasks, str):
66
- tasks = [tasks]
67
- # Verify valid tasks
68
- for t in tasks:
69
- if t not in all_tasks:
70
- raise_error(
71
- f"{t} is not a valid task in the UCLA dataset!"
72
- )
73
- self.tasks = tasks
74
- # The patterns
75
- patterns = {
76
- "BOLD": {
77
- "pattern": (
78
- "{subject}/func/{subject}_task-{task}_bold_space-"
79
- "MNI152NLin2009cAsym_preproc.nii.gz"
80
- ),
81
- "space": "MNI152NLin2009cAsym",
82
- "confounds": {
83
- "pattern": (
84
- "{subject}/func/{subject}_"
85
- "task-{task}_bold_confounds.tsv"
86
- ),
87
- "space": "fmriprep",
88
- },
89
- },
90
- "T1w": {
91
- "pattern": (
92
- "{subject}/anat/{subject}_"
93
- "T1w_space-MNI152NLin2009cAsym_preproc.nii.gz"
94
- ),
95
- "space": "MNI152NLin2009cAsym",
96
- },
97
- "VBM_CSF": {
98
- "pattern": (
99
- "{subject}/anat/{subject}_T1w_space-"
100
- "MNI152NLin2009cAsym_class-CSF_probtissue.nii.gz"
101
- ),
102
- "space": "MNI152NLin2009cAsym",
103
- },
104
- "VBM_GM": {
105
- "pattern": (
106
- "{subject}/anat/{subject}_T1w_space-"
107
- "MNI152NLin2009cAsym_class-GM_probtissue.nii.gz"
108
- ),
109
- "space": "MNI152NLin2009cAsym",
110
- },
111
- "VBM_WM": {
77
+ # the commented out uri leads to new open neuro dataset which does
78
+ # NOT have preprocessed data
79
+ # uri = "https://github.com/OpenNeuroDatasets/ds000030.git"
80
+ datadir: Path = Path("/data/project/psychosis_thalamus/data/fmriprep")
81
+ types: Annotated[_types | list[_types], BeforeValidator(ensure_list)] = [ # noqa: RUF012
82
+ DataType.BOLD,
83
+ DataType.T1w,
84
+ DataType.VBM_CSF,
85
+ DataType.VBM_GM,
86
+ DataType.VBM_WM,
87
+ ]
88
+ tasks: Annotated[
89
+ UCLATask | list[UCLATask], BeforeValidator(ensure_list)
90
+ ] = [ # noqa: RUF012
91
+ UCLATask.REST,
92
+ UCLATask.BART,
93
+ UCLATask.BHT,
94
+ UCLATask.PAMENC,
95
+ UCLATask.PAMRET,
96
+ UCLATask.SCAP,
97
+ UCLATask.TASKSWITCH,
98
+ UCLATask.STOPSIGNAL,
99
+ ]
100
+ patterns: DataGrabberPatterns = { # noqa: RUF012
101
+ "BOLD": {
102
+ "pattern": (
103
+ "{subject}/func/{subject}_task-{task}_bold_space-"
104
+ "MNI152NLin2009cAsym_preproc.nii.gz"
105
+ ),
106
+ "space": "MNI152NLin2009cAsym",
107
+ "confounds": {
112
108
  "pattern": (
113
- "{subject}/anat/{subject}_T1w_space"
114
- "-MNI152NLin2009cAsym_class-WM_probtissue.nii.gz"
109
+ "{subject}/func/{subject}_task-{task}_bold_confounds.tsv"
115
110
  ),
116
- "space": "MNI152NLin2009cAsym",
111
+ "space": "fmriprep",
117
112
  },
118
- }
119
- # Set default types
120
- if types is None:
121
- types = list(patterns.keys())
122
- # Convert single type into list
123
- else:
124
- if not isinstance(types, list):
125
- types = [types]
126
- # The replacements
127
- replacements = ["subject", "task"]
128
- # the commented out uri leads to new open neuro dataset which does
129
- # NOT have preprocessed data
130
- # uri = "https://github.com/OpenNeuroDatasets/ds000030.git"
131
- super().__init__(
132
- types=types,
133
- datadir=datadir,
134
- patterns=patterns,
135
- replacements=replacements,
136
- confounds_format="fmriprep",
137
- )
113
+ },
114
+ "T1w": {
115
+ "pattern": (
116
+ "{subject}/anat/{subject}_"
117
+ "T1w_space-MNI152NLin2009cAsym_preproc.nii.gz"
118
+ ),
119
+ "space": "MNI152NLin2009cAsym",
120
+ },
121
+ "VBM_CSF": {
122
+ "pattern": (
123
+ "{subject}/anat/{subject}_T1w_space-"
124
+ "MNI152NLin2009cAsym_class-CSF_probtissue.nii.gz"
125
+ ),
126
+ "space": "MNI152NLin2009cAsym",
127
+ },
128
+ "VBM_GM": {
129
+ "pattern": (
130
+ "{subject}/anat/{subject}_T1w_space-"
131
+ "MNI152NLin2009cAsym_class-GM_probtissue.nii.gz"
132
+ ),
133
+ "space": "MNI152NLin2009cAsym",
134
+ },
135
+ "VBM_WM": {
136
+ "pattern": (
137
+ "{subject}/anat/{subject}_T1w_space"
138
+ "-MNI152NLin2009cAsym_class-WM_probtissue.nii.gz"
139
+ ),
140
+ "space": "MNI152NLin2009cAsym",
141
+ },
142
+ }
143
+ replacements: list[str] = ["subject", "task"] # noqa: RUF012
144
+ confounds_format: ConfoundsFormat = ConfoundsFormat.FMRIPrep
138
145
 
139
146
  def get_elements(self) -> list:
140
147
  """Implement fetching list of elements in the dataset.
@@ -6,10 +6,13 @@
6
6
  # License: AGPL
7
7
 
8
8
  from pathlib import Path
9
- from typing import Union
9
+ from typing import Literal
10
+
11
+ from pydantic import AnyUrl
10
12
 
11
13
  from ....api.decorators import register_datagrabber
12
- from ....datagrabber import PatternDataladDataGrabber
14
+ from ....datagrabber import DataType, PatternDataladDataGrabber
15
+ from ....typing import DataGrabberPatterns
13
16
 
14
17
 
15
18
  __all__ = ["JuselessDataladUKBVBM"]
@@ -23,29 +26,20 @@ class JuselessDataladUKBVBM(PatternDataladDataGrabber):
23
26
 
24
27
  Parameters
25
28
  ----------
26
- datadir : str or pathlib.Path or None, optional
27
- The directory where the datalad dataset will be cloned. If None,
28
- the datalad dataset will be cloned into a temporary directory
29
- (default None).
29
+ datadir : pathlib.Path, optional
30
+ That path where the datalad dataset will be cloned.
31
+ If not specified, the datalad dataset will be cloned into a temporary
32
+ directory.
30
33
 
31
34
  """
32
35
 
33
- def __init__(self, datadir: Union[str, Path, None] = None) -> None:
34
- uri = "ria+http://ukb.ds.inm7.de#~cat_m0wp1"
35
- rootdir = "m0wp1"
36
- types = ["VBM_GM"]
37
- replacements = ["subject", "session"]
38
- patterns = {
39
- "VBM_GM": {
40
- "pattern": "m0wp1{subject}_ses-{session}_T1w.nii.gz",
41
- "space": "IXI549Space",
42
- },
43
- }
44
- super().__init__(
45
- types=types,
46
- datadir=datadir,
47
- uri=uri,
48
- rootdir=rootdir,
49
- replacements=replacements,
50
- patterns=patterns,
51
- )
36
+ uri: AnyUrl = AnyUrl("ria+http://ukb.ds.inm7.de#~cat_m0wp1")
37
+ rootdir: Path = Path("m0wp1")
38
+ types: list[Literal[DataType.VBM_GM]] = [DataType.VBM_GM] # noqa: RUF012
39
+ patterns: DataGrabberPatterns = { # noqa: RUF012
40
+ "VBM_GM": {
41
+ "pattern": "m0wp1{subject}_ses-{session}_T1w.nii.gz",
42
+ "space": "IXI549Space",
43
+ },
44
+ }
45
+ replacements: list[str] = ["subject", "session"] # noqa: RUF012
File without changes
junifer/conftest.py CHANGED
@@ -4,14 +4,64 @@
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
+ import gc
7
8
  from pathlib import Path
8
9
 
9
10
  import pytest
10
11
 
11
- from junifer.datagrabber import PatternDataladDataGrabber
12
12
  from junifer.utils.singleton import Singleton
13
13
 
14
14
 
15
+ # Vendored packages, whose tests are not collected. Paths are relative to
16
+ # this file, so it also works when testing the installed package.
17
+ _VENDORED = [
18
+ Path(__file__).parent / "external" / "BrainPrint",
19
+ ]
20
+
21
+
22
+ # TODO: replace with ``collect_ignore`` once datalad/datalad#7967 is fixed
23
+ # (see juaml/junifer#518)
24
+ def pytest_ignore_collect(collection_path: Path) -> bool | None:
25
+ """Do not collect the tests of vendored packages.
26
+
27
+ This replaces ``collect_ignore``, which is not applied when datalad's
28
+ pytest plugin (datalad >= 1.4) is installed: its ``pytest_ignore_collect``
29
+ returns False for directories, so pytest's own check is never called.
30
+
31
+ Parameters
32
+ ----------
33
+ collection_path : pathlib.Path
34
+ The path being considered for collection.
35
+
36
+ Returns
37
+ -------
38
+ bool or None
39
+ True to ignore the path, None to let other hooks decide.
40
+
41
+ """
42
+ if any(collection_path.is_relative_to(path) for path in _VENDORED):
43
+ return True
44
+ return None
45
+
46
+
47
+ def pytest_collection_finish(session: pytest.Session) -> None:
48
+ """Exclude the objects alive after collection from garbage collection.
49
+
50
+ nilearn (< 0.14) forces a full garbage collection every time it gets
51
+ the data of an image, which has to go over all the objects of the test
52
+ session (modules, collected tests, etc.). Freezing them makes these
53
+ collections much faster.
54
+
55
+ Parameters
56
+ ----------
57
+ session : pytest.Session
58
+ The pytest session.
59
+
60
+ """
61
+ gc.collect()
62
+ gc.freeze()
63
+
64
+
15
65
  @pytest.fixture(autouse=True)
16
66
  def reset_singletons() -> None:
17
67
  """Reset all singletons."""
@@ -29,29 +79,18 @@ def reset_singletons() -> None:
29
79
  del elem
30
80
 
31
81
 
32
- @pytest.fixture
33
- def maps_datagrabber(tmp_path: Path) -> PatternDataladDataGrabber:
34
- """Return a PatternDataladDataGrabber for maps testing.
82
+ @pytest.fixture(autouse=True)
83
+ def clear_computation_caches() -> None:
84
+ """Clear the cached computations of ReHo and ALFF estimators.
35
85
 
36
- Parameters
37
- ----------
38
- tmp_path : pathlib.Path
39
- The path to the test directory.
86
+ The estimators are singletons whose ``compute`` is cached per input
87
+ path, so results from earlier tests would otherwise be reused.
40
88
 
41
89
  """
42
- dg = PatternDataladDataGrabber(
43
- uri="https://github.com/OpenNeuroDatasets/ds005226.git",
44
- types=["BOLD"],
45
- patterns={
46
- "BOLD": {
47
- "pattern": (
48
- "derivatives/pre-processed_data/space-MNI/{subject}/"
49
- "{subject-padded}_task-{task}_run-{run}_space-MNI152NLin6Asym"
50
- "_res-2_desc-preproc_bold.nii.gz"
51
- ),
52
- "space": "MNI152NLin6Asym",
53
- },
54
- },
55
- replacements=["subject", "subject-padded", "task", "run"],
56
- )
57
- return dg
90
+ from junifer.markers.falff._afni_falff import AFNIALFF
91
+ from junifer.markers.falff._junifer_falff import JuniferALFF
92
+ from junifer.markers.reho._afni_reho import AFNIReHo
93
+ from junifer.markers.reho._junifer_reho import JuniferReHo
94
+
95
+ for klass in (AFNIALFF, JuniferALFF, AFNIReHo, JuniferReHo):
96
+ klass.compute.cache_clear()
junifer/data/_dispatch.py CHANGED
@@ -3,12 +3,11 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from collections.abc import Iterator, MutableMapping
6
+ from collections.abc import Callable, Iterator, MutableMapping
7
7
  from pathlib import Path
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
- Callable,
12
11
  Optional,
13
12
  Union,
14
13
  )
@@ -95,7 +94,7 @@ class DataDispatcher(MutableMapping):
95
94
  # Update global
96
95
  self._registries[key] = value
97
96
 
98
- def popitem():
97
+ def popitem(self):
99
98
  """Not implemented."""
100
99
  pass
101
100
 
@@ -110,14 +109,9 @@ class DataDispatcher(MutableMapping):
110
109
 
111
110
  def get_data(
112
111
  kind: str,
113
- names: Union[
114
- str, # coordinates, parcellation, mask
115
- list[str], # parcellation, mask
116
- dict, # mask
117
- list[dict], # mask
118
- ],
112
+ names: str | list[str] | dict | list[dict],
119
113
  target_data: dict[str, Any],
120
- extra_input: Optional[dict[str, Any]] = None,
114
+ extra_input: dict[str, Any] | None = None,
121
115
  ) -> Union[
122
116
  tuple[ArrayLike, list[str]], # coordinates
123
117
  tuple["Nifti1Image", dict[int, str]], # parcellation
@@ -196,15 +190,11 @@ def load_data(
196
190
  kind: str,
197
191
  name: str,
198
192
  **kwargs,
199
- ) -> Union[
200
- tuple[ArrayLike, list[str], str], # coordinates
201
- tuple[
202
- Optional["Nifti1Image"], list[str], Path, str
203
- ], # parcellation / maps
204
- tuple[
205
- Optional[Union["Nifti1Image", Callable]], Optional[Path], str
206
- ], # mask
207
- ]:
193
+ ) -> (
194
+ tuple[ArrayLike, list[str], str]
195
+ | tuple[Optional["Nifti1Image"], list[str], Path, str]
196
+ | tuple[Union["Nifti1Image", Callable] | None, Path | None, str]
197
+ ):
208
198
  """Load ``kind`` named ``name``.
209
199
 
210
200
  Parameters
@@ -9,7 +9,7 @@ import numpy as np
9
9
  from numpy.typing import ArrayLike
10
10
 
11
11
  from ...pipeline import WorkDirManager
12
- from ...utils import logger, run_ext_cmd
12
+ from ...utils import run_ext_cmd
13
13
 
14
14
 
15
15
  __all__ = ["ANTsCoordinatesWarper"]
@@ -46,6 +46,9 @@ class ANTsCoordinatesWarper:
46
46
  The transformed coordinates.
47
47
 
48
48
  """
49
+ # Imported here to avoid circular import
50
+ from ._coordinates import logger
51
+
49
52
  logger.debug("Using ANTs for coordinates transformation")
50
53
 
51
54
  # Create element-specific tempdir for storing post-warping assets
@@ -4,15 +4,17 @@
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
+ from collections import Counter
7
8
  from pathlib import Path
8
- from typing import Any, Optional
9
+ from typing import Any
9
10
 
10
11
  import numpy as np
11
12
  import pandas as pd
13
+ import structlog
12
14
  from junifer_data import get
13
15
  from numpy.typing import ArrayLike
14
16
 
15
- from ...utils import logger, raise_error
17
+ from ...utils import raise_error
16
18
  from ..pipeline_data_registry_base import BasePipelineDataRegistry
17
19
  from ..utils import JUNIFER_DATA_PARAMS, get_dataset_path, get_native_warper
18
20
  from ._ants_coordinates_warper import ANTsCoordinatesWarper
@@ -21,6 +23,49 @@ from ._fsl_coordinates_warper import FSLCoordinatesWarper
21
23
 
22
24
  __all__ = ["CoordinatesRegistry"]
23
25
 
26
+ _log = structlog.get_logger("junifer")
27
+ logger = _log.bind(pkg="data")
28
+
29
+
30
+ def _sanitize_names(names: list) -> list:
31
+ """Make the VOI names safe to use as feature names.
32
+
33
+ Spaces in the names are replaced with ``_`` and duplicated names get a
34
+ ``-{i}`` suffix, where ``i`` is the (1-based) occurrence of the name,
35
+ skipping suffixes that would clash with another name. Non-string names
36
+ are left untouched.
37
+
38
+ Parameters
39
+ ----------
40
+ names : list
41
+ The names of the VOIs.
42
+
43
+ Returns
44
+ -------
45
+ list
46
+ The sanitized, unique names of the VOIs.
47
+
48
+ """
49
+ names = [x.replace(" ", "_") if isinstance(x, str) else x for x in names]
50
+ counts = Counter(names)
51
+ # Unique names are kept, so suffixed names must not clash with them
52
+ used = {name for name in names if counts[name] == 1}
53
+ next_idx: Counter = Counter()
54
+ out = []
55
+ for name in names:
56
+ if counts[name] > 1:
57
+ # Find the next suffix not in use
58
+ while True:
59
+ next_idx[name] += 1
60
+ new_name = f"{name}-{next_idx[name]}"
61
+ if new_name not in used:
62
+ break
63
+ used.add(new_name)
64
+ out.append(new_name)
65
+ else:
66
+ out.append(name)
67
+ return out
68
+
24
69
 
25
70
  class CoordinatesRegistry(BasePipelineDataRegistry):
26
71
  """Class for coordinates data registry.
@@ -135,7 +180,7 @@ class CoordinatesRegistry(BasePipelineDataRegistry):
135
180
  coordinates: ArrayLike,
136
181
  voi_names: list[str],
137
182
  space: str,
138
- overwrite: Optional[bool] = False,
183
+ overwrite: bool | None = False,
139
184
  ) -> None:
140
185
  """Register a custom user coordinates.
141
186
 
@@ -289,8 +334,8 @@ class CoordinatesRegistry(BasePipelineDataRegistry):
289
334
  )
290
335
  # Convert dataframe to numpy ndarray
291
336
  coords = df_coords.iloc[:, [0, 1, 2]].to_numpy()
292
- # Get label names
293
- names = list(df_coords.iloc[:, [3]].values[:, 0])
337
+ # Get label names, made unique and without spaces
338
+ names = _sanitize_names(list(df_coords.iloc[:, [3]].values[:, 0]))
294
339
  # Load data for external ones
295
340
  else:
296
341
  coords = t_coord["coords"]
@@ -302,7 +347,7 @@ class CoordinatesRegistry(BasePipelineDataRegistry):
302
347
  self,
303
348
  coords: str,
304
349
  target_data: dict[str, Any],
305
- extra_input: Optional[dict[str, Any]] = None,
350
+ extra_input: dict[str, Any] | None = None,
306
351
  ) -> tuple[ArrayLike, list[str]]:
307
352
  """Get coordinates, tailored for the target data.
308
353
 
@@ -9,7 +9,7 @@ import numpy as np
9
9
  from numpy.typing import ArrayLike
10
10
 
11
11
  from ...pipeline import WorkDirManager
12
- from ...utils import logger, run_ext_cmd
12
+ from ...utils import run_ext_cmd
13
13
 
14
14
 
15
15
  __all__ = ["FSLCoordinatesWarper"]
@@ -46,6 +46,9 @@ class FSLCoordinatesWarper:
46
46
  The transformed coordinates.
47
47
 
48
48
  """
49
+ # Imported here to avoid circular import
50
+ from ._coordinates import logger
51
+
49
52
  logger.debug("Using FSL for coordinates transformation")
50
53
 
51
54
  # Create element-specific tempdir for storing post-warping assets
@@ -16,7 +16,10 @@ from junifer.data import (
16
16
  register_data,
17
17
  )
18
18
  from junifer.datareader import DefaultDataReader
19
- from junifer.testing.datagrabbers import OasisVBMTestingDataGrabber
19
+ from junifer.testing.datagrabbers import UCLACNPVBMTestingDataGrabber
20
+
21
+
22
+ pytestmark = pytest.mark.external
20
23
 
21
24
 
22
25
  def test_register_built_in_check() -> None:
@@ -121,6 +124,15 @@ def test_load() -> None:
121
124
  assert space == "MNI"
122
125
 
123
126
 
127
+ def test_load_unique_names() -> None:
128
+ """Test loading coordinates with duplicated names and spaces."""
129
+ _, names, _ = load_data(kind="coordinates", name="Dosenbach")
130
+ assert len(names) == 160 # type: ignore
131
+ assert len(set(names)) == 160 # type: ignore
132
+ assert not any(" " in x for x in names) # type: ignore
133
+ assert names[:5] == ["vmPFC-1", "aPFC-1", "aPFC-2", "mPFC", "aPFC-3"]
134
+
135
+
124
136
  def test_load_nonexisting() -> None:
125
137
  """Test loading coordinates that not exist."""
126
138
  with pytest.raises(ValueError, match=r"not found"):
@@ -130,8 +142,8 @@ def test_load_nonexisting() -> None:
130
142
  def test_get() -> None:
131
143
  """Test tailored coordinates fetch."""
132
144
  reader = DefaultDataReader()
133
- with OasisVBMTestingDataGrabber() as dg:
134
- element = dg["sub-01"]
145
+ with UCLACNPVBMTestingDataGrabber() as dg:
146
+ element = dg["sub-10206"]
135
147
  element_data = reader.fit_transform(element)
136
148
  vbm_gm = element_data["VBM_GM"]
137
149
  # Get tailored coordinates