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
@@ -7,19 +7,23 @@ import logging
7
7
  from pathlib import Path
8
8
 
9
9
  import pytest
10
- import scipy as sp
10
+ from numpy.testing import assert_allclose
11
11
 
12
+ from junifer.datagrabber import DataType
12
13
  from junifer.datareader import DefaultDataReader
13
- from junifer.markers import ReHoParcels
14
+ from junifer.markers import ReHoImpl, ReHoParcels
14
15
  from junifer.pipeline import WorkDirManager
15
- from junifer.pipeline.utils import _check_afni, _check_ants
16
- from junifer.storage import SQLiteFeatureStorage
16
+ from junifer.pipeline.utils import _check_afni
17
+ from junifer.storage import HDF5FeatureStorage
17
18
  from junifer.testing.datagrabbers import (
19
+ ADHDTestingDataGrabber,
18
20
  PartlyCloudyTestingDataGrabber,
19
- SPMAuditoryTestingDataGrabber,
20
21
  )
21
22
 
22
23
 
24
+ pytestmark = pytest.mark.external
25
+
26
+
23
27
  def test_ReHoParcels(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
24
28
  """Test ReHoParcels.
25
29
 
@@ -32,7 +36,7 @@ def test_ReHoParcels(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
32
36
 
33
37
  """
34
38
  with caplog.at_level(logging.DEBUG):
35
- with PartlyCloudyTestingDataGrabber() as dg:
39
+ with PartlyCloudyTestingDataGrabber(n_timepoints=50) as dg:
36
40
  element_data = DefaultDataReader().fit_transform(dg["sub-01"])
37
41
  # Update workdir to current test's tmp_path
38
42
  WorkDirManager().workdir = tmp_path
@@ -40,11 +44,11 @@ def test_ReHoParcels(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
40
44
  # Initialize marker
41
45
  marker = ReHoParcels(
42
46
  parcellation="TianxS1x3TxMNInonlinear2009cAsym",
43
- using="junifer",
47
+ using=ReHoImpl.junifer,
44
48
  )
45
49
  # Check correct output
46
50
  assert "vector" == marker.storage_type(
47
- input_type="BOLD", output_feature="reho"
51
+ input_type=DataType.BOLD, output_feature="reho"
48
52
  )
49
53
 
50
54
  # Fit transform marker on data
@@ -70,7 +74,7 @@ def test_ReHoParcels(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
70
74
  # Reset log capture
71
75
  caplog.clear()
72
76
  # Initialize storage
73
- storage = SQLiteFeatureStorage(tmp_path / "reho_parcels.sqlite")
77
+ storage = HDF5FeatureStorage(uri=tmp_path / "reho_parcels.hdf5")
74
78
  # Fit transform marker on data with storage
75
79
  marker.fit_transform(
76
80
  input=element_data,
@@ -80,29 +84,31 @@ def test_ReHoParcels(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
80
84
  assert "Creating cache" not in caplog.text
81
85
 
82
86
 
83
- @pytest.mark.skipif(
84
- _check_ants() is False, reason="requires ANTs to be in PATH"
85
- )
86
87
  @pytest.mark.skipif(
87
88
  _check_afni() is False, reason="requires AFNI to be in PATH"
88
89
  )
89
- def test_ReHoParcels_comparison(tmp_path: Path) -> None:
90
+ @pytest.mark.parametrize("masks", [None, "compute_brain_mask"])
91
+ def test_ReHoParcels_comparison(tmp_path: Path, masks: str | None) -> None:
90
92
  """Test ReHoParcels implementation comparison.
91
93
 
92
94
  Parameters
93
95
  ----------
94
96
  tmp_path : pathlib.Path
95
97
  The path to the test directory.
98
+ masks : str or None
99
+ The masks to use.
96
100
 
97
101
  """
98
- with SPMAuditoryTestingDataGrabber() as dg:
99
- element_data = DefaultDataReader().fit_transform(dg["sub001"])
102
+ # Use float data with few ties: ReHo is rank-based and the
103
+ # implementations handle tied values differently
104
+ with ADHDTestingDataGrabber() as dg:
105
+ element_data = DefaultDataReader().fit_transform(dg["sub-01"])
100
106
  # Update workdir to current test's tmp_path
101
107
  WorkDirManager().workdir = tmp_path
102
108
 
103
109
  # Initialize marker
104
110
  junifer_marker = ReHoParcels(
105
- parcellation="Schaefer100x7", using="junifer"
111
+ parcellation="Schaefer100x7", using=ReHoImpl.junifer, masks=masks
106
112
  )
107
113
  # Fit transform marker on data
108
114
  junifer_output = junifer_marker.fit_transform(element_data)
@@ -110,15 +116,15 @@ def test_ReHoParcels_comparison(tmp_path: Path) -> None:
110
116
  junifer_output_bold = junifer_output["BOLD"]["reho"]
111
117
 
112
118
  # Initialize marker
113
- afni_marker = ReHoParcels(parcellation="Schaefer100x7", using="afni")
119
+ afni_marker = ReHoParcels(
120
+ parcellation="Schaefer100x7", using=ReHoImpl.afni, masks=masks
121
+ )
114
122
  # Fit transform marker on data
115
123
  afni_output = afni_marker.fit_transform(element_data)
116
124
  # Get BOLD output
117
125
  afni_output_bold = afni_output["BOLD"]["reho"]
118
126
 
119
- # Check for Pearson correlation coefficient
120
- r, _ = sp.stats.pearsonr(
121
- junifer_output_bold["data"].flatten(),
122
- afni_output_bold["data"].flatten(),
127
+ # Both implementations should give the same results
128
+ assert_allclose(
129
+ junifer_output_bold["data"], afni_output_bold["data"], atol=1e-6
123
130
  )
124
- assert r >= 0.2 # this is very bad, but they differ...
@@ -7,16 +7,20 @@ import logging
7
7
  from pathlib import Path
8
8
 
9
9
  import pytest
10
- import scipy as sp
10
+ import scipy.stats as sps
11
11
 
12
+ from junifer.datagrabber import DataType
12
13
  from junifer.datareader import DefaultDataReader
13
- from junifer.markers import ReHoSpheres
14
+ from junifer.markers import ReHoImpl, ReHoSpheres
14
15
  from junifer.pipeline import WorkDirManager
15
16
  from junifer.pipeline.utils import _check_afni
16
- from junifer.storage import SQLiteFeatureStorage
17
+ from junifer.storage import HDF5FeatureStorage
17
18
  from junifer.testing.datagrabbers import SPMAuditoryTestingDataGrabber
18
19
 
19
20
 
21
+ pytestmark = pytest.mark.external
22
+
23
+
20
24
  COORDINATES = "DMNBuckner"
21
25
 
22
26
 
@@ -32,17 +36,17 @@ def test_ReHoSpheres(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
32
36
 
33
37
  """
34
38
  with caplog.at_level(logging.DEBUG):
35
- with SPMAuditoryTestingDataGrabber() as dg:
39
+ with SPMAuditoryTestingDataGrabber(n_timepoints=50) as dg:
36
40
  element_data = DefaultDataReader().fit_transform(dg["sub001"])
37
41
  # Update workdir to current test's tmp_path
38
42
  WorkDirManager().workdir = tmp_path
39
43
  # Initialize marker
40
44
  marker = ReHoSpheres(
41
- coords=COORDINATES, using="junifer", radius=10.0
45
+ coords=COORDINATES, using=ReHoImpl.junifer, radius=10.0
42
46
  )
43
47
  # Check correct output
44
48
  assert "vector" == marker.storage_type(
45
- input_type="BOLD", output_feature="reho"
49
+ input_type=DataType.BOLD, output_feature="reho"
46
50
  )
47
51
 
48
52
  # Fit transform marker on data
@@ -68,7 +72,7 @@ def test_ReHoSpheres(caplog: pytest.LogCaptureFixture, tmp_path: Path) -> None:
68
72
  # Reset log capture
69
73
  caplog.clear()
70
74
  # Initialize storage
71
- storage = SQLiteFeatureStorage(tmp_path / "reho_spheres.sqlite")
75
+ storage = HDF5FeatureStorage(uri=tmp_path / "reho_spheres.hdf5")
72
76
  # Fit transform marker on data with storage
73
77
  marker.fit_transform(
74
78
  input=element_data,
@@ -90,7 +94,7 @@ def test_ReHoSpheres_comparison(tmp_path: Path) -> None:
90
94
  The path to the test directory.
91
95
 
92
96
  """
93
- with SPMAuditoryTestingDataGrabber() as dg:
97
+ with SPMAuditoryTestingDataGrabber(n_timepoints=50) as dg:
94
98
  element_data = DefaultDataReader().fit_transform(dg["sub001"])
95
99
  # Update workdir to current test's tmp_path
96
100
  WorkDirManager().workdir = tmp_path
@@ -98,7 +102,7 @@ def test_ReHoSpheres_comparison(tmp_path: Path) -> None:
98
102
  # Initialize marker
99
103
  junifer_marker = ReHoSpheres(
100
104
  coords=COORDINATES,
101
- using="junifer",
105
+ using=ReHoImpl.junifer,
102
106
  radius=10.0,
103
107
  )
104
108
  # Fit transform marker on data
@@ -109,7 +113,7 @@ def test_ReHoSpheres_comparison(tmp_path: Path) -> None:
109
113
  # Initialize marker
110
114
  afni_marker = ReHoSpheres(
111
115
  coords=COORDINATES,
112
- using="afni",
116
+ using=ReHoImpl.afni,
113
117
  radius=10.0,
114
118
  )
115
119
  # Fit transform marker on data
@@ -118,8 +122,8 @@ def test_ReHoSpheres_comparison(tmp_path: Path) -> None:
118
122
  afni_output_bold = afni_output["BOLD"]["reho"]
119
123
 
120
124
  # Check for Pearson correlation coefficient
121
- r, _ = sp.stats.pearsonr(
125
+ r, _ = sps.pearsonr(
122
126
  junifer_output_bold["data"].flatten(),
123
127
  afni_output_bold["data"].flatten(),
124
128
  )
125
- assert r >= 0.8 # 0.8 is a loose threshold
129
+ assert r >= 0.99
@@ -4,19 +4,36 @@
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
- from typing import Any, ClassVar, Optional, Union
7
+ from typing import Annotated, Any, ClassVar, Literal
8
+
9
+ import numpy as np
10
+ from pydantic import BeforeValidator, PositiveFloat
8
11
 
9
12
  from ..api.decorators import register_marker
10
13
  from ..data import get_data
14
+ from ..datagrabber import DataType
11
15
  from ..external.nilearn import JuniferNiftiSpheresMasker
12
16
  from ..stats import get_aggfunc_by_name
17
+ from ..storage import StorageType
13
18
  from ..typing import Dependencies, MarkerInOutMappings
14
- from ..utils import logger, raise_error, warn_with_log
15
- from .base import BaseMarker
19
+ from ..utils import ensure_list_or_none, raise_error, warn_with_log
20
+ from .base import BaseMarker, logger
16
21
 
17
22
 
18
23
  __all__ = ["SphereAggregation"]
19
24
 
25
+ _on = Literal[
26
+ DataType.T1w,
27
+ DataType.T2w,
28
+ DataType.BOLD,
29
+ DataType.VBM_GM,
30
+ DataType.VBM_WM,
31
+ DataType.VBM_CSF,
32
+ DataType.FALFF,
33
+ DataType.GCOR,
34
+ DataType.LCOR,
35
+ ]
36
+
20
37
 
21
38
  @register_marker
22
39
  class SphereAggregation(BaseMarker):
@@ -27,37 +44,41 @@ class SphereAggregation(BaseMarker):
27
44
  coords : str
28
45
  The name of the coordinates list to use.
29
46
  See :func:`.list_data` for options.
30
- radius : float, optional
31
- The radius of the sphere in millimeters. If None, the signal will be
32
- extracted from a single voxel. See
33
- :class:`nilearn.maskers.NiftiSpheresMasker` for more information
47
+ radius : ``zero`` or positive float or None, optional
48
+ The radius of the sphere in millimeters.
49
+ If None, the signal will be extracted from a single voxel.
50
+ See :class:`.JuniferNiftiSpheresMasker` for more information
34
51
  (default None).
35
52
  allow_overlap : bool, optional
36
- Whether to allow overlapping spheres. If False, an error is raised if
37
- the spheres overlap (default is False).
53
+ Whether to allow overlapping spheres.
54
+ If False, an error is raised if the spheres overlap (default False).
38
55
  method : str, optional
39
- The aggregation method to use.
40
- See :func:`.get_aggfunc_by_name` for more information
56
+ The aggregation function to use.
57
+ See :func:`.get_aggfunc_by_name` for options
41
58
  (default "mean").
42
- method_params : dict, optional
43
- The parameters to pass to the aggregation method (default None).
44
- time_method : str, optional
45
- The method to use to aggregate the time series over the time points,
46
- after applying :term:`method` (only applicable to BOLD data). If None,
59
+ method_params : dict or None, optional
60
+ The parameters to pass to the aggregation function.
61
+ See :func:`.get_aggfunc_by_name` for options (default None).
62
+ time_method : str or None, optional
63
+ The aggregation function to use for time series after applying
64
+ :term:`method` (only applicable to BOLD data). If None,
47
65
  it will not operate on the time dimension (default None).
48
- time_method_params : dict, optional
49
- The parameters to pass to the time aggregation method (default None).
50
- masks : str, dict or list of dict or str, optional
66
+ time_method_params : dict or None, optional
67
+ The parameters to pass to the time aggregation function (default None).
68
+ on : {``DataType.T1w``, ``DataType.T2w``, ``DataType.BOLD``, \
69
+ ``DataType.VBM_GM``, ``DataType.VBM_WM``, ``DataType.VBM_CSF``, \
70
+ ``DataType.FALFF``, ``DataType.GCOR``, ``DataType.LCOR``} or \
71
+ list of them or None, optional
72
+ The data type(s) to apply the marker on.
73
+ If None, will work on all available data.
74
+ Check :enum:`.DataType` for valid values (default None).
75
+ masks : str, dict, list of them or None, optional
51
76
  The specification of the masks to apply to regions before extracting
52
77
  signals. Check :ref:`Using Masks <using_masks>` for more details.
53
78
  If None, will not apply any mask (default None).
54
- on : {"T1w", "T2w", "BOLD", "VBM_GM", "VBM_WM", "VBM_CSF", "fALFF", \
55
- "GCOR", "LCOR"} or list of the options, optional
56
- The data types to apply the marker to. If None, will work on all
57
- available data (default None).
58
- name : str, optional
59
- The name of the marker. By default, it will use KIND_SphereAggregation
60
- where KIND is the kind of data it was applied to (default None).
79
+ name : str or None, optional
80
+ The name of the marker.
81
+ If None, will use the class name (default None).
61
82
 
62
83
  Raises
63
84
  ------
@@ -70,74 +91,68 @@ class SphereAggregation(BaseMarker):
70
91
  _DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "numpy"}
71
92
 
72
93
  _MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
73
- "T1w": {
74
- "aggregation": "vector",
94
+ DataType.T1w: {
95
+ "aggregation": StorageType.Vector,
75
96
  },
76
- "T2w": {
77
- "aggregation": "vector",
97
+ DataType.T2w: {
98
+ "aggregation": StorageType.Vector,
78
99
  },
79
- "BOLD": {
80
- "aggregation": "timeseries",
100
+ DataType.BOLD: {
101
+ "aggregation": StorageType.Timeseries,
81
102
  },
82
- "VBM_GM": {
83
- "aggregation": "vector",
103
+ DataType.VBM_GM: {
104
+ "aggregation": StorageType.Vector,
84
105
  },
85
- "VBM_WM": {
86
- "aggregation": "vector",
106
+ DataType.VBM_WM: {
107
+ "aggregation": StorageType.Vector,
87
108
  },
88
- "VBM_CSF": {
89
- "aggregation": "vector",
109
+ DataType.VBM_CSF: {
110
+ "aggregation": StorageType.Vector,
90
111
  },
91
- "fALFF": {
92
- "aggregation": "vector",
112
+ DataType.FALFF: {
113
+ "aggregation": StorageType.Vector,
93
114
  },
94
- "GCOR": {
95
- "aggregation": "vector",
115
+ DataType.GCOR: {
116
+ "aggregation": StorageType.Vector,
96
117
  },
97
- "LCOR": {
98
- "aggregation": "vector",
118
+ DataType.LCOR: {
119
+ "aggregation": StorageType.Vector,
99
120
  },
100
121
  }
101
122
 
102
- def __init__(
103
- self,
104
- coords: str,
105
- radius: Optional[float] = None,
106
- allow_overlap: bool = False,
107
- method: str = "mean",
108
- method_params: Optional[dict[str, Any]] = None,
109
- time_method: Optional[str] = None,
110
- time_method_params: Optional[dict[str, Any]] = None,
111
- masks: Union[str, dict, list[Union[dict, str]], None] = None,
112
- on: Union[list[str], str, None] = None,
113
- name: Optional[str] = None,
114
- ) -> None:
115
- self.coords = coords
116
- self.radius = radius
117
- self.allow_overlap = allow_overlap
118
- self.method = method
119
- self.method_params = method_params or {}
120
- self.masks = masks
121
- super().__init__(on=on, name=name)
122
-
123
- # Verify after super init so self._on is set
124
- if "BOLD" not in self._on and time_method is not None:
123
+ coords: str
124
+ radius: Literal[0] | PositiveFloat | None = None
125
+ allow_overlap: bool = False
126
+ method: str = "mean"
127
+ method_params: dict[str, Any] | None = None
128
+ time_method: str | None = None
129
+ time_method_params: dict[str, Any] | None = None
130
+ masks: Annotated[
131
+ dict | str | list[dict | str] | None,
132
+ BeforeValidator(ensure_list_or_none),
133
+ ] = None
134
+ on: Annotated[
135
+ _on | list[_on] | None, BeforeValidator(ensure_list_or_none)
136
+ ] = None
137
+
138
+ def validate_marker_params(self) -> None:
139
+ """Run extra logical validation for marker."""
140
+ # self.on is set already
141
+ if "BOLD" not in self.on and self.time_method is not None:
125
142
  raise_error(
126
143
  "`time_method` can only be used with BOLD data. "
127
144
  "Please remove `time_method` parameter."
128
145
  )
129
- if time_method is None and time_method_params is not None:
146
+ if self.time_method is None and self.time_method_params is not None:
130
147
  raise_error(
131
148
  "`time_method_params` can only be used with `time_method`. "
132
149
  "Please remove `time_method_params` parameter."
133
150
  )
134
- self.time_method = time_method
135
- self.time_method_params = time_method_params or {}
136
151
 
137
152
  def compute(
138
153
  self,
139
154
  input: dict[str, Any],
140
- extra_input: Optional[dict] = None,
155
+ extra_input: dict | None = None,
141
156
  ) -> dict:
142
157
  """Compute.
143
158
 
@@ -207,7 +222,8 @@ class SphereAggregation(BaseMarker):
207
222
  agg_func=agg_func,
208
223
  )
209
224
  # Fit and transform the marker on the data
210
- out_values = masker.fit_transform(t_input_img)
225
+ # nilearn maskers return 1D arrays for 3D images, keep 2D
226
+ out_values = np.atleast_2d(masker.fit_transform(t_input_img))
211
227
 
212
228
  # Apply time dimension aggregation if required
213
229
  if self.time_method is not None:
@@ -1,15 +1,19 @@
1
1
  """Provide abstract base class for temporal signal-to-noise ratio (tSNR)."""
2
2
 
3
3
  # Authors: Leonard Sasse <l.sasse@fz-juelich.de>
4
+ # Synchon Mandal <s.mandal@fz-juelich.de>
4
5
  # License: AGPL
5
6
 
6
7
  from abc import abstractmethod
7
- from typing import Any, ClassVar, Optional, Union
8
+ from typing import Annotated, Any, ClassVar
8
9
 
9
10
  from nilearn import image as nimg
11
+ from pydantic import BeforeValidator
10
12
 
13
+ from ...datagrabber import DataType
14
+ from ...storage import StorageType
11
15
  from ...typing import Dependencies, MarkerInOutMappings
12
- from ...utils import raise_error
16
+ from ...utils import ensure_list_or_none, raise_error
13
17
  from ..base import BaseMarker
14
18
 
15
19
 
@@ -22,44 +26,40 @@ class TemporalSNRBase(BaseMarker):
22
26
  Parameters
23
27
  ----------
24
28
  agg_method : str, optional
25
- The method to perform aggregation using. Check valid options in
26
- :func:`.get_aggfunc_by_name` (default "mean").
27
- agg_method_params : dict, optional
28
- Parameters to pass to the aggregation function. Check valid options in
29
- :func:`.get_aggfunc_by_name` (default None).
30
- masks : str, dict or list of dict or str, optional
29
+ The aggregation function to use.
30
+ See :func:`.get_aggfunc_by_name` for options
31
+ (default "mean").
32
+ agg_method_params : dict or None, optional
33
+ The parameters to pass to the aggregation function.
34
+ See :func:`.get_aggfunc_by_name` for options (default None).
35
+ masks : str, dict, list of them or None, optional
31
36
  The specification of the masks to apply to regions before extracting
32
37
  signals. Check :ref:`Using Masks <using_masks>` for more details.
33
38
  If None, will not apply any mask (default None).
34
- name : str, optional
35
- The name of the marker. If None, will use the class name (default
36
- None).
39
+ name : str or None, optional
40
+ The name of the marker.
41
+ If None, will use the class name (default None).
37
42
 
38
43
  """
39
44
 
40
45
  _DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
41
46
 
42
47
  _MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
43
- "BOLD": {
44
- "tsnr": "vector",
48
+ DataType.BOLD: {
49
+ "tsnr": StorageType.Vector,
45
50
  },
46
51
  }
47
52
 
48
- def __init__(
49
- self,
50
- agg_method: str = "mean",
51
- agg_method_params: Optional[dict] = None,
52
- masks: Union[str, dict, list[Union[dict, str]], None] = None,
53
- name: Optional[str] = None,
54
- ) -> None:
55
- self.agg_method = agg_method
56
- self.agg_method_params = agg_method_params
57
- self.masks = masks
58
- super().__init__(on="BOLD", name=name)
53
+ agg_method: str = "mean"
54
+ agg_method_params: dict | None = None
55
+ masks: Annotated[
56
+ dict | str | list[dict | str] | None,
57
+ BeforeValidator(ensure_list_or_none),
58
+ ] = None
59
59
 
60
60
  @abstractmethod
61
61
  def aggregate(
62
- self, input: dict[str, Any], extra_input: Optional[dict] = None
62
+ self, input: dict[str, Any], extra_input: dict | None = None
63
63
  ) -> dict[str, Any]:
64
64
  """Perform aggregation."""
65
65
  raise_error(
@@ -70,7 +70,7 @@ class TemporalSNRBase(BaseMarker):
70
70
  def compute(
71
71
  self,
72
72
  input: dict[str, Any],
73
- extra_input: Optional[dict] = None,
73
+ extra_input: dict | None = None,
74
74
  ) -> dict:
75
75
  """Compute.
76
76
 
@@ -104,7 +104,9 @@ class TemporalSNRBase(BaseMarker):
104
104
  "img.std(axis=-1).squeeze()", img=input["data"]
105
105
  )
106
106
  mask_img = nimg.math_img("(stdv_img != 0)", stdv_img=stdv_img)
107
- input["data"] = nimg.math_img(
107
+ # Aggregate the tSNR image, without changing the input
108
+ aggregation_input = dict(input.items())
109
+ aggregation_input["data"] = nimg.math_img(
108
110
  "np.divide(mean_img, stdv_img, where=mask_img.astype(bool))",
109
111
  mean_img=mean_img,
110
112
  stdv_img=stdv_img,
@@ -113,8 +115,8 @@ class TemporalSNRBase(BaseMarker):
113
115
  # Perform necessary aggregation and return
114
116
  return {
115
117
  "tsnr": {
116
- **self.aggregate(input=input, extra_input=extra_input)[
117
- "aggregation"
118
- ]
118
+ **self.aggregate(
119
+ input=aggregation_input, extra_input=extra_input
120
+ )["aggregation"]
119
121
  }
120
122
  }
@@ -3,9 +3,10 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from typing import Any, Optional, Union
6
+ from typing import Any
7
7
 
8
8
  from ...api.decorators import register_marker
9
+ from ...datagrabber import DataType
9
10
  from ..maps_aggregation import MapsAggregation
10
11
  from .temporal_snr_base import TemporalSNRBase
11
12
 
@@ -22,30 +23,20 @@ class TemporalSNRMaps(TemporalSNRBase):
22
23
  maps : str
23
24
  The name of the map(s) to use.
24
25
  See :func:`.list_data` for options.
25
- masks : str, dict or list of dict or str, optional
26
+ masks : str, dict, list of them or None, optional
26
27
  The specification of the masks to apply to regions before extracting
27
28
  signals. Check :ref:`Using Masks <using_masks>` for more details.
28
29
  If None, will not apply any mask (default None).
29
- name : str, optional
30
- The name of the marker. If None, will use the class name (default
31
- None).
30
+ name : str or None, optional
31
+ The name of the marker.
32
+ If None, will use the class name (default None).
32
33
 
33
34
  """
34
35
 
35
- def __init__(
36
- self,
37
- maps: str,
38
- masks: Union[str, dict, list[Union[dict, str]], None] = None,
39
- name: Optional[str] = None,
40
- ) -> None:
41
- self.maps = maps
42
- super().__init__(
43
- masks=masks,
44
- name=name,
45
- )
36
+ maps: str
46
37
 
47
38
  def aggregate(
48
- self, input: dict[str, Any], extra_input: Optional[dict] = None
39
+ self, input: dict[str, Any], extra_input: dict | None = None
49
40
  ) -> dict:
50
41
  """Perform maps aggregation.
51
42
 
@@ -76,5 +67,5 @@ class TemporalSNRMaps(TemporalSNRBase):
76
67
  return MapsAggregation(
77
68
  maps=self.maps,
78
69
  masks=self.masks,
79
- on="BOLD",
70
+ on=DataType.BOLD,
80
71
  ).compute(input=input, extra_input=extra_input)