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
@@ -0,0 +1,279 @@
1
+ """Provide tests for markers on regions lost in resampling."""
2
+
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from collections.abc import Iterator
7
+ from importlib.util import find_spec
8
+ from pathlib import Path
9
+
10
+ import nibabel as nib
11
+ import numpy as np
12
+ import pytest
13
+
14
+ from junifer import markers
15
+ from junifer.data import deregister_data, register_data
16
+ from junifer.datareader import DefaultDataReader
17
+ from junifer.pipeline import WorkDirManager
18
+
19
+
20
+ COMPLEXITY_MARKERS = (
21
+ "HurstExponent",
22
+ "RangeEntropy",
23
+ "RangeEntropyAUC",
24
+ "MultiscaleEntropyAUC",
25
+ "PermEntropy",
26
+ "WeightedPermEntropy",
27
+ "SampleEntropy",
28
+ )
29
+
30
+ skip_no_neurokit2 = pytest.mark.skipif(
31
+ find_spec("neurokit2") is None, reason="requires neurokit2"
32
+ )
33
+
34
+
35
+ @pytest.fixture(scope="module")
36
+ def element(tmp_path_factory: pytest.TempPathFactory) -> Iterator[dict]:
37
+ """Provide BOLD data where regions of the parcellations are lost.
38
+
39
+ The parcellations are on a 1mm grid and the BOLD data on a 3mm grid,
40
+ which does not sample their single-voxel regions:
41
+
42
+ * ``LostRegion`` (and ``LostRegion2``, the same): regions ``a``, ``b``,
43
+ ``lost``, ``c`` and ``d``.
44
+ * ``OneRegionLeft``: regions ``kept``, ``lost`` and ``lost_too``.
45
+
46
+ Parameters
47
+ ----------
48
+ tmp_path_factory : pytest.TempPathFactory
49
+ The pytest factory for temporary directories.
50
+
51
+ Yields
52
+ ------
53
+ dict
54
+ The element data.
55
+
56
+ """
57
+ tmp_path = tmp_path_factory.mktemp("lost_regions")
58
+ # Voxels 0, 3, 6, ... are sampled by the 3mm grid, 7 and 13 are not
59
+ lost_region = np.zeros((24, 24, 24), dtype=np.int16)
60
+ lost_region[0:12, 0:12] = 1
61
+ lost_region[12:24, 0:12] = 2
62
+ lost_region[7, 13, 7] = 3
63
+ lost_region[0:12, 15:24] = 4
64
+ lost_region[12:24, 15:24] = 5
65
+ one_region_left = np.zeros((24, 24, 24), dtype=np.int16)
66
+ one_region_left[0:12, 0:12] = 1
67
+ one_region_left[7, 13, 7] = 2
68
+ one_region_left[13, 7, 7] = 3
69
+ for name, data, labels in (
70
+ ("LostRegion", lost_region, ["a", "b", "lost", "c", "d"]),
71
+ ("LostRegion2", lost_region, ["a", "b", "lost", "c", "d"]),
72
+ ("OneRegionLeft", one_region_left, ["kept", "lost", "lost_too"]),
73
+ ):
74
+ path = tmp_path / f"{name}.nii.gz"
75
+ nib.save(nib.Nifti1Image(data, np.eye(4)), path)
76
+ register_data(
77
+ kind="parcellation",
78
+ name=name,
79
+ parcellation_path=path,
80
+ parcels_labels=labels,
81
+ space="MNI152NLin6Asym",
82
+ )
83
+ # Random walks, so all the measures are defined
84
+ rng = np.random.default_rng(0)
85
+ bold = rng.normal(size=(8, 8, 8, 120)).cumsum(axis=-1) + 1000
86
+ bold_img = nib.Nifti1Image(
87
+ bold.astype(np.float32), np.diag([3.0, 3.0, 3.0, 1.0])
88
+ )
89
+ bold_img.header.set_xyzt_units("mm", "sec")
90
+ bold_path = tmp_path / "bold.nii.gz"
91
+ nib.save(bold_img, bold_path)
92
+ yield DefaultDataReader().fit_transform(
93
+ {"BOLD": {"path": bold_path, "space": "MNI152NLin6Asym"}}
94
+ )
95
+ for name in ("LostRegion", "LostRegion2", "OneRegionLeft"):
96
+ deregister_data(kind="parcellation", name=name)
97
+
98
+
99
+ def _case(name: str, params: dict, feature: str, nan: list) -> object:
100
+ """Get a test case.
101
+
102
+ Parameters
103
+ ----------
104
+ name : str
105
+ The name of the marker class.
106
+ params : dict
107
+ The parameters of the marker.
108
+ feature : str
109
+ The name of the feature to check.
110
+ nan : list
111
+ The values that are NaN.
112
+
113
+ Returns
114
+ -------
115
+ object
116
+ The test case, named after the parcellation, marker and feature.
117
+ Complexity markers are skipped if neurokit2 is not installed.
118
+
119
+ """
120
+ parcellation = params.get("parcellation", params.get("parcellation_one"))
121
+ return pytest.param(
122
+ name,
123
+ params,
124
+ feature,
125
+ nan,
126
+ marks=skip_no_neurokit2 if name in COMPLEXITY_MARKERS else (),
127
+ id=f"{parcellation}-{name}-{feature}",
128
+ )
129
+
130
+
131
+ # Whether the values are NaN for LostRegion, whose 3rd region is lost
132
+ REGIONS = [False, False, True, False, False]
133
+ # For connectivity, a region pair is NaN if one of the regions is lost
134
+ PAIRS = [[r or c for c in REGIONS] for r in REGIONS]
135
+ # For edge-centric FC, the edges are the region pairs (lower triangle)
136
+ EDGES = [r or c for i, r in enumerate(REGIONS) for c in REGIONS[:i]]
137
+ EDGE_PAIRS = [[r or c for c in EDGES] for r in EDGES]
138
+
139
+
140
+ @pytest.mark.parametrize(
141
+ "name, params, feature, nan",
142
+ [
143
+ _case(
144
+ "ParcelAggregation",
145
+ {"parcellation": "LostRegion", "method": "mean", "on": "BOLD"},
146
+ "aggregation",
147
+ [REGIONS] * 120,
148
+ ),
149
+ _case(
150
+ "FunctionalConnectivityParcels",
151
+ {"parcellation": "LostRegion"},
152
+ "functional_connectivity",
153
+ PAIRS,
154
+ ),
155
+ _case(
156
+ "EdgeCentricFCParcels",
157
+ {"parcellation": "LostRegion"},
158
+ "functional_connectivity",
159
+ EDGE_PAIRS,
160
+ ),
161
+ _case(
162
+ "CrossParcellationFC",
163
+ {
164
+ "parcellation_one": "LostRegion",
165
+ "parcellation_two": "LostRegion2",
166
+ },
167
+ "functional_connectivity",
168
+ PAIRS,
169
+ ),
170
+ _case(
171
+ "ReHoParcels",
172
+ {"parcellation": "LostRegion", "using": "junifer"},
173
+ "reho",
174
+ [REGIONS],
175
+ ),
176
+ _case(
177
+ "ALFFParcels",
178
+ {"parcellation": "LostRegion", "using": "junifer"},
179
+ "alff",
180
+ [REGIONS],
181
+ ),
182
+ _case(
183
+ "ALFFParcels",
184
+ {"parcellation": "LostRegion", "using": "junifer"},
185
+ "falff",
186
+ [REGIONS],
187
+ ),
188
+ _case(
189
+ "TemporalSNRParcels",
190
+ {"parcellation": "LostRegion"},
191
+ "tsnr",
192
+ [REGIONS],
193
+ ),
194
+ # Computed with the edges with data
195
+ _case(
196
+ "RSSETSMarker",
197
+ {"parcellation": "LostRegion"},
198
+ "rss_ets",
199
+ [[False]] * 120,
200
+ ),
201
+ *[
202
+ _case(
203
+ name, {"parcellation": "LostRegion"}, "complexity", [REGIONS]
204
+ )
205
+ for name in COMPLEXITY_MARKERS
206
+ ],
207
+ # Only the kept region with itself
208
+ _case(
209
+ "FunctionalConnectivityParcels",
210
+ {"parcellation": "OneRegionLeft"},
211
+ "functional_connectivity",
212
+ [[False, True, True], [True, True, True], [True, True, True]],
213
+ ),
214
+ # All the edges have a lost region
215
+ _case(
216
+ "EdgeCentricFCParcels",
217
+ {"parcellation": "OneRegionLeft"},
218
+ "functional_connectivity",
219
+ [[True] * 3] * 3,
220
+ ),
221
+ # No edges with data
222
+ _case(
223
+ "RSSETSMarker",
224
+ {"parcellation": "OneRegionLeft"},
225
+ "rss_ets",
226
+ [[True]] * 120,
227
+ ),
228
+ *[
229
+ _case(
230
+ name,
231
+ {"parcellation": "OneRegionLeft"},
232
+ "complexity",
233
+ [[False, True, True]],
234
+ )
235
+ for name in COMPLEXITY_MARKERS
236
+ ],
237
+ ],
238
+ )
239
+ def test_lost_regions(
240
+ tmp_path: Path,
241
+ element: dict,
242
+ name: str,
243
+ params: dict,
244
+ feature: str,
245
+ nan: list,
246
+ ) -> None:
247
+ """Test markers on regions lost in resampling.
248
+
249
+ The lost regions have no data (NaN), and the other regions are not
250
+ affected.
251
+
252
+ Parameters
253
+ ----------
254
+ tmp_path : pathlib.Path
255
+ The path to the test directory.
256
+ element : dict
257
+ The element data.
258
+ name : str
259
+ The parametrized name of the marker class.
260
+ params : dict
261
+ The parametrized parameters of the marker.
262
+ feature : str
263
+ The parametrized name of the feature to check.
264
+ nan : list
265
+ The parametrized values that are NaN.
266
+
267
+ """
268
+ if name in COMPLEXITY_MARKERS:
269
+ from junifer.markers import complexity
270
+
271
+ marker = getattr(complexity, name)(**params)
272
+ else:
273
+ marker = getattr(markers, name)(**params)
274
+ WorkDirManager().workdir = tmp_path
275
+ with pytest.warns(RuntimeWarning, match=r"region\(s\) .* have no voxels"):
276
+ # Copy the data, as some markers change their input
277
+ out = marker.fit_transform({"BOLD": dict(element["BOLD"])})
278
+ data = np.asarray(out["BOLD"][feature]["data"], dtype=float)
279
+ assert np.array_equal(np.isnan(data), nan)
@@ -12,63 +12,67 @@ from numpy.testing import assert_array_almost_equal, assert_array_equal
12
12
 
13
13
  from junifer.data import MapsRegistry, MaskRegistry
14
14
  from junifer.data.masks import compute_brain_mask
15
- from junifer.datagrabber import PatternDataladDataGrabber
15
+ from junifer.datagrabber import DataType
16
16
  from junifer.datareader import DefaultDataReader
17
17
  from junifer.markers import MapsAggregation
18
- from junifer.storage import HDF5FeatureStorage
18
+ from junifer.storage import HDF5FeatureStorage, StorageType
19
+ from junifer.testing.datagrabbers import ADHDTestingDataGrabber
20
+
21
+
22
+ pytestmark = pytest.mark.external
19
23
 
20
24
 
21
25
  @pytest.mark.parametrize(
22
26
  "input_type, storage_type",
23
27
  [
24
28
  (
25
- "T1w",
26
- "vector",
29
+ DataType.T1w,
30
+ StorageType.Vector,
27
31
  ),
28
32
  (
29
- "T2w",
30
- "vector",
33
+ DataType.T2w,
34
+ StorageType.Vector,
31
35
  ),
32
36
  (
33
- "BOLD",
34
- "timeseries",
37
+ DataType.BOLD,
38
+ StorageType.Timeseries,
35
39
  ),
36
40
  (
37
- "VBM_GM",
38
- "vector",
41
+ DataType.VBM_GM,
42
+ StorageType.Vector,
39
43
  ),
40
44
  (
41
- "VBM_WM",
42
- "vector",
45
+ DataType.VBM_WM,
46
+ StorageType.Vector,
43
47
  ),
44
48
  (
45
- "VBM_CSF",
46
- "vector",
49
+ DataType.VBM_CSF,
50
+ StorageType.Vector,
47
51
  ),
48
52
  (
49
- "fALFF",
50
- "vector",
53
+ DataType.FALFF,
54
+ StorageType.Vector,
51
55
  ),
52
56
  (
53
- "GCOR",
54
- "vector",
57
+ DataType.GCOR,
58
+ StorageType.Vector,
55
59
  ),
56
60
  (
57
- "LCOR",
58
- "vector",
61
+ DataType.LCOR,
62
+ StorageType.Vector,
59
63
  ),
60
64
  ],
61
65
  )
62
66
  def test_MapsAggregation_input_output(
63
- input_type: str, storage_type: str
67
+ input_type: DataType, storage_type: StorageType
64
68
  ) -> None:
65
69
  """Test MapsAggregation input and output types.
66
70
 
67
71
  Parameters
68
72
  ----------
69
- input_type : str
73
+ input_type : DataType
70
74
  The parametrized input type.
71
- storage_type : str
75
+ storage_type : StorageType
72
76
  The parametrized storage type.
73
77
 
74
78
  """
@@ -77,19 +81,10 @@ def test_MapsAggregation_input_output(
77
81
  ).storage_type(input_type=input_type, output_feature="aggregation")
78
82
 
79
83
 
80
- def test_MapsAggregation_3D(
81
- maps_datagrabber: PatternDataladDataGrabber,
82
- ) -> None:
83
- """Test MapsAggregation on 3D data.
84
-
85
- Parameters
86
- ----------
87
- maps_datagrabber : PatternDataladDataGrabber
88
- The testing PatternDataladDataGrabber, as fixture.
89
-
90
- """
91
- with maps_datagrabber as dg:
92
- element = dg[("sub-01", "sub-001", "rest", "1")]
84
+ def test_MapsAggregation_3D() -> None:
85
+ """Test MapsAggregation on 3D data."""
86
+ with ADHDTestingDataGrabber() as dg:
87
+ element = dg["sub-01"]
93
88
  element_data = DefaultDataReader().fit_transform(element)
94
89
  # Deepcopy data for later use
95
90
  input_data = deepcopy(element_data)
@@ -122,19 +117,10 @@ def test_MapsAggregation_3D(
122
117
  assert_array_equal(nifti_maps_masked_bold, maps_agg_bold_data)
123
118
 
124
119
 
125
- def test_MapsAggregation_4D(
126
- maps_datagrabber: PatternDataladDataGrabber,
127
- ) -> None:
128
- """Test MapsAggregation on 4D data.
129
-
130
- Parameters
131
- ----------
132
- maps_datagrabber : PatternDataladDataGrabber
133
- The testing PatternDataladDataGrabber, as fixture.
134
-
135
- """
136
- with maps_datagrabber as dg:
137
- element = dg[("sub-01", "sub-001", "rest", "1")]
120
+ def test_MapsAggregation_4D() -> None:
121
+ """Test MapsAggregation on 4D data."""
122
+ with ADHDTestingDataGrabber() as dg:
123
+ element = dg["sub-01"]
138
124
  element_data = DefaultDataReader().fit_transform(element)
139
125
  # Deepcopy data for later use
140
126
  input_data = deepcopy(element_data)
@@ -162,29 +148,25 @@ def test_MapsAggregation_4D(
162
148
  assert_array_equal(nifti_maps_masked_bold, maps_agg_bold_data)
163
149
 
164
150
 
165
- def test_MapsAggregation_storage(
166
- maps_datagrabber: PatternDataladDataGrabber, tmp_path: Path
167
- ) -> None:
151
+ def test_MapsAggregation_storage(tmp_path: Path) -> None:
168
152
  """Test MapsAggregation storage.
169
153
 
170
154
  Parameters
171
155
  ----------
172
- maps_datagrabber : PatternDataladDataGrabber
173
- The testing PatternDataladDataGrabber, as fixture.
174
156
  tmp_path : pathlib.Path
175
157
  The path to the test directory.
176
158
 
177
159
  """
178
160
  # Store 3D
179
- with maps_datagrabber as dg:
180
- element = dg[("sub-01", "sub-001", "rest", "1")]
161
+ with ADHDTestingDataGrabber() as dg:
162
+ element = dg["sub-01"]
181
163
  element_data = DefaultDataReader().fit_transform(element)
182
164
  storage = HDF5FeatureStorage(
183
165
  uri=tmp_path / "test_maps_storage_3D.hdf5"
184
166
  )
185
167
  marker = MapsAggregation(
186
168
  maps="Smith_rsn_10",
187
- on="BOLD",
169
+ on=DataType.BOLD,
188
170
  )
189
171
  element_data["BOLD"]["data"] = element_data["BOLD"]["data"].slicer[
190
172
  ..., 0:1
@@ -197,15 +179,15 @@ def test_MapsAggregation_storage(
197
179
  )
198
180
 
199
181
  # Store 4D
200
- with maps_datagrabber as dg:
201
- element = dg[("sub-01", "sub-001", "rest", "1")]
182
+ with ADHDTestingDataGrabber() as dg:
183
+ element = dg["sub-01"]
202
184
  element_data = DefaultDataReader().fit_transform(element)
203
185
  storage = HDF5FeatureStorage(
204
- uri=tmp_path / "test_maps_storage_4D.sqlite"
186
+ uri=tmp_path / "test_maps_storage_4D.hdf5"
205
187
  )
206
188
  marker = MapsAggregation(
207
189
  maps="Smith_rsn_10",
208
- on="BOLD",
190
+ on=DataType.BOLD,
209
191
  )
210
192
  marker.fit_transform(input=element_data, storage=storage)
211
193
  features = storage.list_features()
@@ -215,19 +197,10 @@ def test_MapsAggregation_storage(
215
197
  )
216
198
 
217
199
 
218
- def test_MapsAggregation_3D_mask(
219
- maps_datagrabber: PatternDataladDataGrabber,
220
- ) -> None:
221
- """Test MapsAggregation on 3D data with mask.
222
-
223
- Parameters
224
- ----------
225
- maps_datagrabber : PatternDataladDataGrabber
226
- The testing PatternDataladDataGrabber, as fixture.
227
-
228
- """
229
- with maps_datagrabber as dg:
230
- element = dg[("sub-01", "sub-001", "rest", "1")]
200
+ def test_MapsAggregation_3D_mask() -> None:
201
+ """Test MapsAggregation on 3D data with mask."""
202
+ with ADHDTestingDataGrabber() as dg:
203
+ element = dg["sub-01"]
231
204
  element_data = DefaultDataReader().fit_transform(element)
232
205
  # Deepcopy data for later use
233
206
  input_data = deepcopy(element_data)
@@ -238,8 +211,8 @@ def test_MapsAggregation_3D_mask(
238
211
  ]
239
212
  marker = MapsAggregation(
240
213
  maps="Smith_rsn_10",
241
- on="BOLD",
242
- masks="compute_brain_mask",
214
+ on=DataType.BOLD,
215
+ masks=["compute_brain_mask"],
243
216
  )
244
217
  maps_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
245
218
  "aggregation"
@@ -268,19 +241,10 @@ def test_MapsAggregation_3D_mask(
268
241
  assert_array_equal(nifti_maps_masked_bold, maps_agg_bold_data)
269
242
 
270
243
 
271
- def test_MapsAggregation_3D_mask_computed(
272
- maps_datagrabber: PatternDataladDataGrabber,
273
- ) -> None:
274
- """Test MapsAggregation on 3D data with computed masks.
275
-
276
- Parameters
277
- ----------
278
- maps_datagrabber : PatternDataladDataGrabber
279
- The testing PatternDataladDataGrabber, as fixture.
280
-
281
- """
282
- with maps_datagrabber as dg:
283
- element = dg[("sub-01", "sub-001", "rest", "1")]
244
+ def test_MapsAggregation_3D_mask_computed() -> None:
245
+ """Test MapsAggregation on 3D data with computed masks."""
246
+ with ADHDTestingDataGrabber() as dg:
247
+ element = dg["sub-01"]
284
248
  element_data = DefaultDataReader().fit_transform(element)
285
249
 
286
250
  # Compare with nilearn
@@ -306,8 +270,8 @@ def test_MapsAggregation_3D_mask_computed(
306
270
  # Use the MapsAggregation object
307
271
  marker = MapsAggregation(
308
272
  maps="Smith_rsn_10",
309
- masks={"compute_brain_mask": {"threshold": 0.2}},
310
- on="BOLD",
273
+ masks=[{"compute_brain_mask": {"threshold": 0.2}}],
274
+ on=DataType.BOLD,
311
275
  )
312
276
  maps_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
313
277
  "aggregation"
@@ -324,25 +288,16 @@ def test_MapsAggregation_3D_mask_computed(
324
288
  )
325
289
 
326
290
 
327
- def test_MapsAggregation_4D_agg_time(
328
- maps_datagrabber: PatternDataladDataGrabber,
329
- ):
330
- """Test MapsAggregation on 4D data, aggregating time.
331
-
332
- Parameters
333
- ----------
334
- maps_datagrabber : PatternDataladDataGrabber
335
- The testing PatternDataladDataGrabber, as fixture.
336
-
337
- """
338
- with maps_datagrabber as dg:
339
- element = dg[("sub-01", "sub-001", "rest", "1")]
291
+ def test_MapsAggregation_4D_agg_time() -> None:
292
+ """Test MapsAggregation on 4D data, aggregating time."""
293
+ with ADHDTestingDataGrabber() as dg:
294
+ element = dg["sub-01"]
340
295
  element_data = DefaultDataReader().fit_transform(element)
341
296
  # Create MapsAggregation object
342
297
  marker = MapsAggregation(
343
298
  maps="Smith_rsn_10",
344
299
  time_method="mean",
345
- on="BOLD",
300
+ on=DataType.BOLD,
346
301
  )
347
302
  maps_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
348
303
  "aggregation"
@@ -375,7 +330,7 @@ def test_MapsAggregation_4D_agg_time(
375
330
  maps="Smith_rsn_10",
376
331
  time_method="select",
377
332
  time_method_params={"pick": [0]},
378
- on="BOLD",
333
+ on=DataType.BOLD,
379
334
  )
380
335
  maps_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
381
336
  "aggregation"
@@ -395,7 +350,7 @@ def test_MapsAggregation_errors() -> None:
395
350
  maps="Smith_rsn_10",
396
351
  time_method="select",
397
352
  time_method_params={"pick": [0]},
398
- on="VBM_GM",
353
+ on=[DataType.VBM_GM],
399
354
  )
400
355
 
401
356
  with pytest.raises(
@@ -404,16 +359,14 @@ def test_MapsAggregation_errors() -> None:
404
359
  MapsAggregation(
405
360
  maps="Smith_rsn_10",
406
361
  time_method_params={"pick": [0]},
407
- on="VBM_GM",
362
+ on=[DataType.VBM_GM],
408
363
  )
409
364
 
410
365
 
411
- def test_MapsAggregation_warning(
412
- maps_datagrabber: PatternDataladDataGrabber,
413
- ) -> None:
366
+ def test_MapsAggregation_warning() -> None:
414
367
  """Test warning for MapsAggregation."""
415
- with maps_datagrabber as dg:
416
- element = dg[("sub-01", "sub-001", "rest", "1")]
368
+ with ADHDTestingDataGrabber() as dg:
369
+ element = dg["sub-01"]
417
370
  element_data = DefaultDataReader().fit_transform(element)
418
371
  with pytest.warns(
419
372
  RuntimeWarning, match="No time dimension to aggregate"
@@ -422,7 +375,7 @@ def test_MapsAggregation_warning(
422
375
  maps="Smith_rsn_10",
423
376
  time_method="select",
424
377
  time_method_params={"pick": [0]},
425
- on="BOLD",
378
+ on=DataType.BOLD,
426
379
  )
427
380
  element_data["BOLD"]["data"] = element_data["BOLD"]["data"].slicer[
428
381
  ..., 0:1
@@ -6,10 +6,14 @@
6
6
  # Synchon Mandal <s.mandal@fz-juelich.de>
7
7
  # License: AGPL
8
8
 
9
+ from pathlib import Path
10
+
11
+ import nibabel as nib
9
12
  import numpy as np
10
13
  import pytest
11
14
 
12
- from junifer.markers.utils import _ets
15
+ from junifer.markers.utils import _ets, _save_mask
16
+ from junifer.pipeline import WorkDirManager
13
17
 
14
18
 
15
19
  def test_ets() -> None:
@@ -48,3 +52,38 @@ def test_ets_incorrect_label_list() -> None:
48
52
  ValueError, match="List of roi names does not correspond"
49
53
  ):
50
54
  _ets(bold_ts, roi_labels)
55
+
56
+
57
+ def test_save_mask(tmp_path: Path) -> None:
58
+ """Test saving masks in the element directory, named by their content.
59
+
60
+ Parameters
61
+ ----------
62
+ tmp_path : pathlib.Path
63
+ The path to the test directory.
64
+
65
+ """
66
+ workdir_mgr = WorkDirManager()
67
+ workdir_mgr.workdir = tmp_path
68
+ data = np.zeros((3, 4, 5), dtype=np.int8)
69
+ data[1, 1:3, 2:4] = 1
70
+ mask = nib.Nifti1Image(data, np.eye(4))
71
+ path = _save_mask(mask, prefix="test_mask")
72
+ # In the element directory, with the voxels in the mask
73
+ assert path.parent == workdir_mgr.elementdir
74
+ assert path.name.startswith("test_mask_")
75
+ assert np.array_equal(np.asarray(nib.load(path).dataobj), data)
76
+ # The same mask (another image) has the same path, a different one not
77
+ assert (
78
+ _save_mask(nib.Nifti1Image(data.copy(), np.eye(4)), prefix="test_mask")
79
+ == path
80
+ )
81
+ other = data.copy()
82
+ other[0, 0, 0] = 1
83
+ assert (
84
+ _save_mask(nib.Nifti1Image(other, np.eye(4)), prefix="test_mask")
85
+ != path
86
+ )
87
+ # Deleted with the element directory
88
+ workdir_mgr.cleanup_elementdir()
89
+ assert not path.exists()