junifer 0.0.5.dev240__py3-none-any.whl → 0.0.6__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 (279) hide show
  1. junifer/__init__.py +2 -31
  2. junifer/__init__.pyi +37 -0
  3. junifer/_version.py +9 -4
  4. junifer/api/__init__.py +3 -5
  5. junifer/api/__init__.pyi +4 -0
  6. junifer/api/decorators.py +14 -19
  7. junifer/api/functions.py +165 -109
  8. junifer/api/py.typed +0 -0
  9. junifer/api/queue_context/__init__.py +2 -4
  10. junifer/api/queue_context/__init__.pyi +5 -0
  11. junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
  12. junifer/api/queue_context/htcondor_adapter.py +23 -6
  13. junifer/api/queue_context/py.typed +0 -0
  14. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
  15. junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
  16. junifer/api/tests/test_functions.py +168 -74
  17. junifer/cli/__init__.py +24 -0
  18. junifer/cli/__init__.pyi +3 -0
  19. junifer/{api → cli}/cli.py +141 -125
  20. junifer/cli/parser.py +235 -0
  21. junifer/cli/py.typed +0 -0
  22. junifer/{api → cli}/tests/test_cli.py +8 -8
  23. junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
  24. junifer/{api → cli}/tests/test_parser.py +2 -2
  25. junifer/{api → cli}/utils.py +6 -16
  26. junifer/configs/juseless/__init__.py +2 -2
  27. junifer/configs/juseless/__init__.pyi +3 -0
  28. junifer/configs/juseless/datagrabbers/__init__.py +2 -12
  29. junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
  30. junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
  31. junifer/configs/juseless/datagrabbers/py.typed +0 -0
  32. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
  33. junifer/configs/juseless/datagrabbers/ucla.py +4 -4
  34. junifer/configs/juseless/py.typed +0 -0
  35. junifer/conftest.py +25 -0
  36. junifer/data/__init__.py +2 -42
  37. junifer/data/__init__.pyi +29 -0
  38. junifer/data/_dispatch.py +248 -0
  39. junifer/data/coordinates/__init__.py +9 -0
  40. junifer/data/coordinates/__init__.pyi +5 -0
  41. junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
  42. junifer/data/coordinates/_coordinates.py +385 -0
  43. junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
  44. junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
  45. junifer/data/masks/__init__.py +9 -0
  46. junifer/data/masks/__init__.pyi +6 -0
  47. junifer/data/masks/_ants_mask_warper.py +177 -0
  48. junifer/data/masks/_fsl_mask_warper.py +106 -0
  49. junifer/data/masks/_masks.py +802 -0
  50. junifer/data/{tests → masks/tests}/test_masks.py +67 -63
  51. junifer/data/parcellations/__init__.py +9 -0
  52. junifer/data/parcellations/__init__.pyi +6 -0
  53. junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
  54. junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
  55. junifer/data/parcellations/_parcellations.py +1388 -0
  56. junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
  57. junifer/data/pipeline_data_registry_base.py +76 -0
  58. junifer/data/py.typed +0 -0
  59. junifer/data/template_spaces.py +44 -79
  60. junifer/data/tests/test_data_utils.py +1 -2
  61. junifer/data/tests/test_template_spaces.py +8 -4
  62. junifer/data/utils.py +109 -4
  63. junifer/datagrabber/__init__.py +2 -26
  64. junifer/datagrabber/__init__.pyi +27 -0
  65. junifer/datagrabber/aomic/__init__.py +2 -4
  66. junifer/datagrabber/aomic/__init__.pyi +5 -0
  67. junifer/datagrabber/aomic/id1000.py +81 -52
  68. junifer/datagrabber/aomic/piop1.py +83 -55
  69. junifer/datagrabber/aomic/piop2.py +85 -56
  70. junifer/datagrabber/aomic/py.typed +0 -0
  71. junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
  72. junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
  73. junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
  74. junifer/datagrabber/base.py +22 -18
  75. junifer/datagrabber/datalad_base.py +71 -34
  76. junifer/datagrabber/dmcc13_benchmark.py +31 -18
  77. junifer/datagrabber/hcp1200/__init__.py +2 -3
  78. junifer/datagrabber/hcp1200/__init__.pyi +4 -0
  79. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
  80. junifer/datagrabber/hcp1200/hcp1200.py +26 -15
  81. junifer/datagrabber/hcp1200/py.typed +0 -0
  82. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
  83. junifer/datagrabber/multiple.py +14 -9
  84. junifer/datagrabber/pattern.py +132 -96
  85. junifer/datagrabber/pattern_validation_mixin.py +206 -94
  86. junifer/datagrabber/py.typed +0 -0
  87. junifer/datagrabber/tests/test_datalad_base.py +27 -12
  88. junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
  89. junifer/datagrabber/tests/test_multiple.py +48 -2
  90. junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
  91. junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
  92. junifer/datareader/__init__.py +2 -2
  93. junifer/datareader/__init__.pyi +3 -0
  94. junifer/datareader/default.py +6 -6
  95. junifer/datareader/py.typed +0 -0
  96. junifer/external/nilearn/__init__.py +2 -3
  97. junifer/external/nilearn/__init__.pyi +4 -0
  98. junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
  99. junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
  100. junifer/external/nilearn/py.typed +0 -0
  101. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
  102. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
  103. junifer/markers/__init__.py +2 -38
  104. junifer/markers/__init__.pyi +37 -0
  105. junifer/markers/base.py +11 -14
  106. junifer/markers/brainprint.py +12 -14
  107. junifer/markers/complexity/__init__.py +2 -18
  108. junifer/markers/complexity/__init__.pyi +17 -0
  109. junifer/markers/complexity/complexity_base.py +9 -11
  110. junifer/markers/complexity/hurst_exponent.py +7 -7
  111. junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
  112. junifer/markers/complexity/perm_entropy.py +7 -7
  113. junifer/markers/complexity/py.typed +0 -0
  114. junifer/markers/complexity/range_entropy.py +7 -7
  115. junifer/markers/complexity/range_entropy_auc.py +7 -7
  116. junifer/markers/complexity/sample_entropy.py +7 -7
  117. junifer/markers/complexity/tests/test_complexity_base.py +1 -1
  118. junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
  119. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
  120. junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
  121. junifer/markers/complexity/tests/test_range_entropy.py +5 -5
  122. junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
  123. junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
  124. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
  125. junifer/markers/complexity/weighted_perm_entropy.py +7 -7
  126. junifer/markers/ets_rss.py +12 -11
  127. junifer/markers/falff/__init__.py +2 -3
  128. junifer/markers/falff/__init__.pyi +4 -0
  129. junifer/markers/falff/_afni_falff.py +38 -45
  130. junifer/markers/falff/_junifer_falff.py +16 -19
  131. junifer/markers/falff/falff_base.py +7 -11
  132. junifer/markers/falff/falff_parcels.py +9 -9
  133. junifer/markers/falff/falff_spheres.py +8 -8
  134. junifer/markers/falff/py.typed +0 -0
  135. junifer/markers/falff/tests/test_falff_spheres.py +3 -1
  136. junifer/markers/functional_connectivity/__init__.py +2 -12
  137. junifer/markers/functional_connectivity/__init__.pyi +13 -0
  138. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
  139. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
  140. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
  141. junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
  142. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
  143. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
  144. junifer/markers/functional_connectivity/py.typed +0 -0
  145. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
  146. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
  147. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
  148. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
  149. junifer/markers/parcel_aggregation.py +22 -17
  150. junifer/markers/py.typed +0 -0
  151. junifer/markers/reho/__init__.py +2 -3
  152. junifer/markers/reho/__init__.pyi +4 -0
  153. junifer/markers/reho/_afni_reho.py +29 -35
  154. junifer/markers/reho/_junifer_reho.py +13 -14
  155. junifer/markers/reho/py.typed +0 -0
  156. junifer/markers/reho/reho_base.py +7 -11
  157. junifer/markers/reho/reho_parcels.py +10 -10
  158. junifer/markers/reho/reho_spheres.py +9 -9
  159. junifer/markers/sphere_aggregation.py +22 -17
  160. junifer/markers/temporal_snr/__init__.py +2 -3
  161. junifer/markers/temporal_snr/__init__.pyi +4 -0
  162. junifer/markers/temporal_snr/py.typed +0 -0
  163. junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
  164. junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
  165. junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
  166. junifer/markers/tests/test_ets_rss.py +3 -3
  167. junifer/markers/tests/test_parcel_aggregation.py +24 -24
  168. junifer/markers/tests/test_sphere_aggregation.py +6 -6
  169. junifer/markers/utils.py +3 -3
  170. junifer/onthefly/__init__.py +2 -1
  171. junifer/onthefly/_brainprint.py +138 -0
  172. junifer/onthefly/read_transform.py +5 -8
  173. junifer/pipeline/__init__.py +2 -10
  174. junifer/pipeline/__init__.pyi +13 -0
  175. junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
  176. junifer/pipeline/pipeline_component_registry.py +294 -0
  177. junifer/pipeline/pipeline_step_mixin.py +15 -11
  178. junifer/pipeline/py.typed +0 -0
  179. junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
  180. junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
  181. junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
  182. junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
  183. junifer/pipeline/tests/test_workdir_manager.py +43 -0
  184. junifer/pipeline/update_meta_mixin.py +21 -17
  185. junifer/pipeline/utils.py +6 -6
  186. junifer/pipeline/workdir_manager.py +19 -5
  187. junifer/preprocess/__init__.py +2 -10
  188. junifer/preprocess/__init__.pyi +11 -0
  189. junifer/preprocess/base.py +10 -10
  190. junifer/preprocess/confounds/__init__.py +2 -2
  191. junifer/preprocess/confounds/__init__.pyi +3 -0
  192. junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
  193. junifer/preprocess/confounds/py.typed +0 -0
  194. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
  195. junifer/preprocess/py.typed +0 -0
  196. junifer/preprocess/smoothing/__init__.py +2 -2
  197. junifer/preprocess/smoothing/__init__.pyi +3 -0
  198. junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
  199. junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
  200. junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
  201. junifer/preprocess/smoothing/py.typed +0 -0
  202. junifer/preprocess/smoothing/smoothing.py +11 -13
  203. junifer/preprocess/warping/__init__.py +2 -2
  204. junifer/preprocess/warping/__init__.pyi +3 -0
  205. junifer/preprocess/warping/_ants_warper.py +136 -32
  206. junifer/preprocess/warping/_fsl_warper.py +73 -22
  207. junifer/preprocess/warping/py.typed +0 -0
  208. junifer/preprocess/warping/space_warper.py +39 -11
  209. junifer/preprocess/warping/tests/test_space_warper.py +5 -9
  210. junifer/py.typed +0 -0
  211. junifer/stats.py +5 -5
  212. junifer/storage/__init__.py +2 -10
  213. junifer/storage/__init__.pyi +11 -0
  214. junifer/storage/base.py +47 -13
  215. junifer/storage/hdf5.py +95 -33
  216. junifer/storage/pandas_base.py +12 -11
  217. junifer/storage/py.typed +0 -0
  218. junifer/storage/sqlite.py +11 -11
  219. junifer/storage/tests/test_hdf5.py +86 -4
  220. junifer/storage/tests/test_sqlite.py +2 -2
  221. junifer/storage/tests/test_storage_base.py +5 -2
  222. junifer/storage/tests/test_utils.py +33 -7
  223. junifer/storage/utils.py +95 -9
  224. junifer/testing/__init__.py +2 -3
  225. junifer/testing/__init__.pyi +4 -0
  226. junifer/testing/datagrabbers.py +10 -11
  227. junifer/testing/py.typed +0 -0
  228. junifer/testing/registry.py +4 -7
  229. junifer/testing/tests/test_testing_registry.py +9 -17
  230. junifer/tests/test_stats.py +2 -2
  231. junifer/typing/__init__.py +9 -0
  232. junifer/typing/__init__.pyi +31 -0
  233. junifer/typing/_typing.py +68 -0
  234. junifer/utils/__init__.py +2 -12
  235. junifer/utils/__init__.pyi +18 -0
  236. junifer/utils/_config.py +110 -0
  237. junifer/utils/_yaml.py +16 -0
  238. junifer/utils/helpers.py +6 -6
  239. junifer/utils/logging.py +117 -8
  240. junifer/utils/py.typed +0 -0
  241. junifer/{pipeline → utils}/singleton.py +19 -14
  242. junifer/utils/tests/test_config.py +59 -0
  243. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
  244. junifer-0.0.6.dist-info/RECORD +350 -0
  245. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
  246. junifer-0.0.6.dist-info/entry_points.txt +2 -0
  247. junifer/api/parser.py +0 -118
  248. junifer/data/coordinates.py +0 -408
  249. junifer/data/masks.py +0 -670
  250. junifer/data/parcellations.py +0 -1828
  251. junifer/pipeline/registry.py +0 -177
  252. junifer/pipeline/tests/test_registry.py +0 -150
  253. junifer-0.0.5.dev240.dist-info/RECORD +0 -275
  254. junifer-0.0.5.dev240.dist-info/entry_points.txt +0 -2
  255. /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
  256. /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
  257. /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
  258. /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
  259. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
  260. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
  261. /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
  262. /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
  263. /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
  264. /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
  265. /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
  266. /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
  267. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
  268. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
  269. /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
  270. /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
  271. /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
  272. /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
  273. /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
  274. /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
  275. /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
  276. /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
  277. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
  278. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
  279. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
1
+ """Provide onthefly functions for BrainPrint post-analysis."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from typing import Optional
7
+
8
+ import numpy as np
9
+ import pandas as pd
10
+
11
+ from ..typing import StorageLike
12
+ from ..utils import raise_error
13
+
14
+
15
+ __all__ = ["normalize", "reweight"]
16
+
17
+
18
+ def normalize(
19
+ storage: StorageLike,
20
+ features: dict[str, dict[str, Optional[str]]],
21
+ kind: str,
22
+ ) -> pd.DataFrame:
23
+ """Read stored brainprint data and normalize either surfaces or volumes.
24
+
25
+ Parameters
26
+ ----------
27
+ storage : storage-like
28
+ The storage class, for example, :class:`.HDF5FeatureStorage`.
29
+ features : dict, optional
30
+ The feature names or MD5 hashes to read as dict.
31
+ The dict should have the keys:
32
+
33
+ * ``"areas"`` (if ``kind="surface"``)
34
+ * ``"volumes"`` (if ``kind="volume"``)
35
+ * ``"eigenvalues"``
36
+
37
+ and the corresponding value for each of the keys is again
38
+ a dict with the keys:
39
+
40
+ * ``"feature_name"`` : str or None
41
+ * ``"feature_md5"`` : str or None
42
+
43
+ Either one of ``"feature_name"`` or ``"feature_md5"`` needs to be
44
+ not None for each first-level key, but both keys are mandatory.
45
+
46
+ kind : {"surface", "volume"}
47
+ The kind of normalization.
48
+
49
+ Returns
50
+ -------
51
+ pandas.DataFrame
52
+ The transformed feature as a ``pandas.DataFrame``.
53
+
54
+ Raises
55
+ ------
56
+ ValueError
57
+ If ``kind`` is invalid.
58
+
59
+ """
60
+ # Read storage
61
+ data_dict = {}
62
+ for k, v in features.items():
63
+ data_dict[k] = storage.read_df(**v) # type: ignore
64
+
65
+ # Check and normalize
66
+ valid_kind = ["surface", "volume"]
67
+ normalized_df = None
68
+ if kind == "surface":
69
+ eigenvalues_df = data_dict["eigenvalues"]
70
+ areas_df = data_dict["areas"]
71
+ normalized_df = eigenvalues_df.combine(
72
+ areas_df, lambda left, right: left * right
73
+ )
74
+ elif kind == "volume":
75
+ eigenvalues_df = data_dict["eigenvalues"]
76
+ volumes_df = data_dict["volumes"]
77
+ normalized_df = eigenvalues_df.combine(
78
+ volumes_df, lambda left, right: left * right ** np.divide(2.0, 3.0)
79
+ )
80
+ else:
81
+ raise_error(
82
+ "Invalid value for `kind`, should be one of: " f"{valid_kind}"
83
+ )
84
+
85
+ return normalized_df
86
+
87
+
88
+ def reweight(
89
+ storage: StorageLike,
90
+ feature_name: Optional[str] = None,
91
+ feature_md5: Optional[str] = None,
92
+ ) -> pd.DataFrame:
93
+ """Read stored brainprint data and reweight eigenvalues.
94
+
95
+ Parameters
96
+ ----------
97
+ storage : storage-like
98
+ The storage class, for example, :class:`.HDF5FeatureStorage`.
99
+ feature_name : str, optional
100
+ Name of the feature to read (default None).
101
+ feature_md5 : str, optional
102
+ MD5 hash of the feature to read (default None).
103
+
104
+ Returns
105
+ -------
106
+ pandas.DataFrame
107
+ The transformed feature as a ``pandas.DataFrame``.
108
+
109
+ """
110
+ # Read storage
111
+ eigenvalues_df = storage.read_df(
112
+ feature_name=feature_name, feature_md5=feature_md5
113
+ ) # type: ignore
114
+
115
+ # Create data for operation
116
+ exploded_count_idx_df = (
117
+ eigenvalues_df.reset_index("eigenvalue")
118
+ .index.value_counts()
119
+ .apply(lambda x: np.arange(1, x + 1).astype(float))
120
+ .to_frame()
121
+ .explode("count")
122
+ )
123
+ idx_data = (
124
+ pd.concat(
125
+ [exploded_count_idx_df] * len(eigenvalues_df.columns), axis=1
126
+ )
127
+ .reset_index()
128
+ .drop("subject", axis=1, inplace=False)
129
+ .to_numpy()
130
+ )
131
+ idx_df = pd.DataFrame(
132
+ data=idx_data,
133
+ index=eigenvalues_df.index,
134
+ columns=eigenvalues_df.columns,
135
+ )
136
+
137
+ # Combine
138
+ return eigenvalues_df.combine(idx_df, lambda left, right: left / right)
@@ -4,27 +4,24 @@
4
4
  # License: AGPL
5
5
 
6
6
 
7
- from typing import TYPE_CHECKING, Dict, Optional, Tuple, Type
7
+ from typing import Optional
8
8
 
9
9
  import pandas as pd
10
10
 
11
+ from ..typing import StorageLike
11
12
  from ..utils import logger, raise_error, warn_with_log
12
13
 
13
14
 
14
- if TYPE_CHECKING:
15
- from junifer.storage import BaseFeatureStorage
16
-
17
-
18
15
  __all__ = ["read_transform"]
19
16
 
20
17
 
21
18
  def read_transform(
22
- storage: Type["BaseFeatureStorage"],
19
+ storage: StorageLike,
23
20
  transform: str,
24
21
  feature_name: Optional[str] = None,
25
22
  feature_md5: Optional[str] = None,
26
- transform_args: Optional[Tuple] = None,
27
- transform_kw_args: Optional[Dict] = None,
23
+ transform_args: Optional[tuple] = None,
24
+ transform_kw_args: Optional[dict] = None,
28
25
  ) -> pd.DataFrame:
29
26
  """Read stored feature and transform to specific statistical output.
30
27
 
@@ -3,15 +3,7 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from . import registry
7
- from .pipeline_step_mixin import PipelineStepMixin
8
- from .update_meta_mixin import UpdateMetaMixin
9
- from .workdir_manager import WorkDirManager
6
+ import lazy_loader as lazy
10
7
 
11
8
 
12
- __all__ = [
13
- "registry",
14
- "PipelineStepMixin",
15
- "UpdateMetaMixin",
16
- "WorkDirManager",
17
- ]
9
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,13 @@
1
+ __all__ = [
2
+ "PipelineComponentRegistry",
3
+ "PipelineStepMixin",
4
+ "UpdateMetaMixin",
5
+ "WorkDirManager",
6
+ "MarkerCollection",
7
+ ]
8
+
9
+ from .pipeline_component_registry import PipelineComponentRegistry
10
+ from .pipeline_step_mixin import PipelineStepMixin
11
+ from .update_meta_mixin import UpdateMetaMixin
12
+ from .workdir_manager import WorkDirManager
13
+ from .marker_collection import MarkerCollection
@@ -5,20 +5,14 @@
5
5
  # License: AGPL
6
6
 
7
7
  from collections import Counter
8
- from typing import TYPE_CHECKING, Dict, List, Optional
8
+ from typing import Optional
9
9
 
10
- from ..datareader.default import DefaultDataReader
11
- from ..markers.base import BaseMarker
10
+ from ..datareader import DefaultDataReader
12
11
  from ..pipeline import PipelineStepMixin, WorkDirManager
13
- from ..preprocess.base import BasePreprocessor
14
- from ..storage.base import BaseFeatureStorage
12
+ from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
15
13
  from ..utils import logger, raise_error
16
14
 
17
15
 
18
- if TYPE_CHECKING:
19
- from junifer.datagrabber import BaseDataGrabber
20
-
21
-
22
16
  __all__ = ["MarkerCollection"]
23
17
 
24
18
 
@@ -45,10 +39,10 @@ class MarkerCollection:
45
39
 
46
40
  def __init__(
47
41
  self,
48
- markers: List[BaseMarker],
42
+ markers: list[MarkerLike],
49
43
  datareader: Optional[PipelineStepMixin] = None,
50
- preprocessors: Optional[List[BasePreprocessor]] = None,
51
- storage: Optional[BaseFeatureStorage] = None,
44
+ preprocessors: Optional[list[PreprocessorLike]] = None,
45
+ storage: Optional[StorageLike] = None,
52
46
  ):
53
47
  # Check that the markers have different names
54
48
  marker_names = [m.name for m in markers]
@@ -65,7 +59,7 @@ class MarkerCollection:
65
59
  self._preprocessors = preprocessors
66
60
  self._storage = storage
67
61
 
68
- def fit(self, input: Dict[str, Dict]) -> Optional[Dict]:
62
+ def fit(self, input: dict[str, dict]) -> Optional[dict]:
69
63
  """Fit the pipeline.
70
64
 
71
65
  Parameters
@@ -111,7 +105,7 @@ class MarkerCollection:
111
105
 
112
106
  return None if self._storage else out
113
107
 
114
- def validate(self, datagrabber: "BaseDataGrabber") -> None:
108
+ def validate(self, datagrabber: DataGrabberLike) -> None:
115
109
  """Validate the pipeline.
116
110
 
117
111
  Without doing any computation, check if the marker collection can
@@ -0,0 +1,294 @@
1
+ """Provide a class for centralized pipeline component registry."""
2
+
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # Leonard Sasse <l.sasse@fz-juelich.de>
5
+ # Synchon Mandal <s.mandal@fz-juelich.de>
6
+ # License: AGPL
7
+
8
+ import importlib
9
+ from collections.abc import Mapping
10
+ from typing import Optional, Union
11
+
12
+ from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
13
+ from ..utils import logger, raise_error
14
+ from ..utils.singleton import Singleton
15
+
16
+
17
+ __all__ = ["PipelineComponentRegistry"]
18
+
19
+
20
+ class PipelineComponentRegistry(metaclass=Singleton):
21
+ """Class for pipeline component registry.
22
+
23
+ This class is a singleton and is used for managing pipeline components.
24
+ It serves as a centralized registry for built-in and third-party pipeline
25
+ components like datagrabbers, datareaders, preprocessors, markers and
26
+ storage.
27
+
28
+ Attributes
29
+ ----------
30
+ steps : list of str
31
+ Valid pipeline steps.
32
+ components : dict
33
+ Registered components for valid pipeline steps.
34
+
35
+ """
36
+
37
+ def __init__(self) -> None:
38
+ """Initialize the class."""
39
+ # Valid steps for operation
40
+ self._steps = [
41
+ "datagrabber",
42
+ "datareader",
43
+ "preprocessing",
44
+ "marker",
45
+ "storage",
46
+ ]
47
+ # Step to sub-package mapping
48
+ self._step_to_subpkg_mappings = {
49
+ "datagrabber": "datagrabber",
50
+ "datareader": "datareader",
51
+ "preprocessing": "preprocess",
52
+ "marker": "markers",
53
+ "storage": "storage",
54
+ }
55
+ # Component registry for valid steps
56
+ self._components = {
57
+ "datagrabber": {
58
+ "HCP1200": "HCP1200",
59
+ "BaseDataGrabber": "BaseDataGrabber",
60
+ "DataladAOMICID1000": "DataladAOMICID1000",
61
+ "DataladAOMICPIOP1": "DataladAOMICPIOP1",
62
+ "DataladAOMICPIOP2": "DataladAOMICPIOP2",
63
+ "DataladDataGrabber": "DataladDataGrabber",
64
+ "DataladHCP1200": "DataladHCP1200",
65
+ "DMCC13Benchmark": "DMCC13Benchmark",
66
+ "MultipleDataGrabber": "MultipleDataGrabber",
67
+ "PatternDataGrabber": "PatternDataGrabber",
68
+ "PatternDataladDataGrabber": "PatternDataladDataGrabber",
69
+ },
70
+ "datareader": {
71
+ "DefaultDataReader": "DefaultDataReader",
72
+ },
73
+ "preprocessing": {
74
+ "BasePreprocessor": "BasePreprocessor",
75
+ "Smoothing": "Smoothing",
76
+ "SpaceWarper": "SpaceWarper",
77
+ "fMRIPrepConfoundRemover": "fMRIPrepConfoundRemover",
78
+ },
79
+ "marker": {
80
+ "ALFFParcels": "ALFFParcels",
81
+ "ALFFSpheres": "ALFFSpheres",
82
+ "BaseMarker": "BaseMarker",
83
+ "BrainPrint": "BrainPrint",
84
+ "CrossParcellationFC": "CrossParcellationFC",
85
+ "EdgeCentricFCParcels": "EdgeCentricFCParcels",
86
+ "EdgeCentricFCSpheres": "EdgeCentricFCSpheres",
87
+ "FunctionalConnectivityParcels": (
88
+ "FunctionalConnectivityParcels"
89
+ ),
90
+ "FunctionalConnectivitySpheres": (
91
+ "FunctionalConnectivitySpheres"
92
+ ),
93
+ "ParcelAggregation": "ParcelAggregation",
94
+ "ReHoParcels": "ReHoParcels",
95
+ "ReHoSpheres": "ReHoSpheres",
96
+ "RSSETSMarker": "RSSETSMarker",
97
+ "SphereAggregation": "SphereAggregation",
98
+ "TemporalSNRParcels": "TemporalSNRParcels",
99
+ "TemporalSNRSpheres": "TemporalSNRSpheres",
100
+ },
101
+ "storage": {
102
+ "BaseFeatureStorage": "BaseFeatureStorage",
103
+ "HDF5FeatureStorage": "HDF5FeatureStorage",
104
+ "PandasBaseFeatureStorage": "PandasBaseFeatureStorage",
105
+ "SQLiteFeatureStorage": "SQLiteFeatureStorage",
106
+ },
107
+ }
108
+
109
+ def _check_valid_step(self, step: str) -> None:
110
+ """Check if ``step`` is valid."""
111
+ if step not in self._steps:
112
+ raise_error(msg=f"Invalid step: {step}", klass=ValueError)
113
+
114
+ @property
115
+ def steps(self) -> list[str]:
116
+ """Get valid pipeline steps."""
117
+ return self._steps
118
+
119
+ @property
120
+ def components(self) -> Mapping[str, Mapping[str, Union[str, type]]]:
121
+ """Get registered components for valid pipeline steps."""
122
+ return self._components
123
+
124
+ def register(self, step: str, klass: type) -> None:
125
+ """Register ``klass`` under ``step``.
126
+
127
+ Parameters
128
+ ----------
129
+ step : str
130
+ Name of the pipeline step. For valid steps, check :meth:`.steps`.
131
+ klass : class
132
+ Class to be registered.
133
+
134
+ Raises
135
+ ------
136
+ ValueError
137
+ If the ``step`` is invalid.
138
+
139
+ """
140
+ # Verify step
141
+ self._check_valid_step(step)
142
+ # Log and register
143
+ name = klass.__name__
144
+ logger.info(f"Registering {name} in {step}")
145
+ self._components[step][name] = klass
146
+
147
+ def deregister(self, step: str, klass: type) -> None:
148
+ """De-register ``klass`` under ``step``.
149
+
150
+ Parameters
151
+ ----------
152
+ step : str
153
+ Name of the pipeline step. For valid steps, check :meth:`.steps`.
154
+ klass : class
155
+ Class to be de-registered.
156
+
157
+ Raises
158
+ ------
159
+ ValueError
160
+ If the ``step`` is invalid.
161
+
162
+ """
163
+ # Verify step
164
+ self._check_valid_step(step)
165
+ # Log and de-register
166
+ name = klass.__name__
167
+ logger.info(f"De-registering {name} in {step}")
168
+ _ = self._components[step].pop(name)
169
+
170
+ def step_components(self, step: str) -> list[str]:
171
+ """Get registered components for ``step``.
172
+
173
+ Parameters
174
+ ----------
175
+ step : str
176
+ Name of the pipeline step.
177
+
178
+ Returns
179
+ -------
180
+ list of str
181
+ List of registered component classes.
182
+
183
+ Raises
184
+ ------
185
+ ValueError
186
+ If the ``step`` is invalid.
187
+
188
+ """
189
+ # Verify step
190
+ self._check_valid_step(step)
191
+
192
+ return list(self._components[step].keys())
193
+
194
+ def get_class(self, step: str, name: str) -> type:
195
+ """Get the class registered under ``name`` for ``step``.
196
+
197
+ Parameters
198
+ ----------
199
+ step : str
200
+ Name of the pipeline step.
201
+ name : str
202
+ Name of the component.
203
+
204
+ Returns
205
+ -------
206
+ class
207
+ Registered class.
208
+
209
+ Raises
210
+ ------
211
+ ValueError
212
+ If the ``step`` or ``name`` is invalid.
213
+
214
+ """
215
+ # Verify step
216
+ self._check_valid_step(step)
217
+ # Verify step name
218
+ if name not in self._components[step]:
219
+ raise_error(msg=f"Invalid name: {name}", klass=ValueError)
220
+
221
+ # Check if first-time import, then import it
222
+ if isinstance(self._components[step][name], str):
223
+ klass = getattr(
224
+ importlib.import_module(
225
+ f"junifer.{self._step_to_subpkg_mappings[step]}"
226
+ ),
227
+ name,
228
+ )
229
+ else:
230
+ klass = self._components[step][name]
231
+
232
+ return klass
233
+
234
+ def build_component_instance(
235
+ self,
236
+ step: str,
237
+ name: str,
238
+ baseclass: type,
239
+ init_params: Optional[dict] = None,
240
+ ) -> Union[DataGrabberLike, PreprocessorLike, MarkerLike, StorageLike]:
241
+ """Build an instance of class registered as ``name``.
242
+
243
+ Parameters
244
+ ----------
245
+ step : str
246
+ Name of the pipeline step.
247
+ name : str
248
+ Name of the component.
249
+ baseclass : class
250
+ Base class to be checked against.
251
+ init_params : dict or None, optional
252
+ Parameters to pass to the class constructor (default None).
253
+
254
+ Returns
255
+ -------
256
+ object
257
+ An instance of the class registered as ``name`` under ``step``.
258
+
259
+ Raises
260
+ ------
261
+ RuntimeError
262
+ If there is a problem creating the instance.
263
+ ValueError
264
+ If the created object with the given name is not a subclass of the
265
+ base class ``baseclass``.
266
+
267
+ """
268
+ # Set default init parameters
269
+ if init_params is None:
270
+ init_params = {}
271
+ # Get registered class
272
+ logger.debug(f"Building {step}/{name}")
273
+ klass = self.get_class(step=step, name=name)
274
+ logger.debug(f"\tClass: {klass.__name__}")
275
+ logger.debug(f"\tInit params: {init_params}")
276
+ try:
277
+ # Create instance of the class
278
+ object_ = klass(**init_params)
279
+ except (ValueError, TypeError) as e:
280
+ raise_error(
281
+ msg=f"Failed to create {step} ({name}). Error: {e}",
282
+ klass=RuntimeError,
283
+ exception=e,
284
+ )
285
+ # Verify created instance belongs to the base class
286
+ if not isinstance(object_, baseclass):
287
+ raise_error(
288
+ msg=(
289
+ f"Invalid {step} ({object_.__class__.__name__}). "
290
+ f"Must inherit from {baseclass.__name__}"
291
+ ),
292
+ klass=ValueError,
293
+ )
294
+ return object_
@@ -15,7 +15,7 @@ else:
15
15
 
16
16
  from importlib.util import find_spec
17
17
  from itertools import chain
18
- from typing import Any, Dict, List
18
+ from typing import Any
19
19
 
20
20
  from ..utils import raise_error
21
21
  from .utils import check_ext_dependencies
@@ -27,7 +27,7 @@ __all__ = ["PipelineStepMixin"]
27
27
  class PipelineStepMixin:
28
28
  """Mixin class for a pipeline step."""
29
29
 
30
- def validate_input(self, input: List[str]) -> List[str]:
30
+ def validate_input(self, input: list[str]) -> list[str]:
31
31
  """Validate the input to the pipeline step.
32
32
 
33
33
  Parameters
@@ -74,8 +74,8 @@ class PipelineStepMixin:
74
74
 
75
75
  def _fit_transform(
76
76
  self,
77
- input: Dict[str, Dict],
78
- ) -> Dict[str, Dict]:
77
+ input: dict[str, dict],
78
+ ) -> dict[str, dict]:
79
79
  """Fit and transform.
80
80
 
81
81
  Parameters
@@ -94,7 +94,7 @@ class PipelineStepMixin:
94
94
  klass=NotImplementedError,
95
95
  ) # pragma: no cover
96
96
 
97
- def validate(self, input: List[str]) -> List[str]:
97
+ def validate(self, input: list[str]) -> list[str]:
98
98
  """Validate the the pipeline step.
99
99
 
100
100
  Parameters
@@ -196,10 +196,14 @@ class PipelineStepMixin:
196
196
  for dependency in obj._CONDITIONAL_DEPENDENCIES:
197
197
  if dependency["using"] == obj.using:
198
198
  depends_on = dependency["depends_on"]
199
- # Check dependencies
200
- _check_dependencies(depends_on)
201
- # Check external dependencies
202
- _check_ext_dependencies(depends_on)
199
+ # Conditional to make `using="auto"` work
200
+ if not isinstance(depends_on, list):
201
+ depends_on = [depends_on]
202
+ for entry in depends_on:
203
+ # Check dependencies
204
+ _check_dependencies(entry)
205
+ # Check external dependencies
206
+ _check_ext_dependencies(entry)
203
207
 
204
208
  # Check dependencies
205
209
  _check_dependencies(self)
@@ -224,8 +228,8 @@ class PipelineStepMixin:
224
228
  return outputs
225
229
 
226
230
  def fit_transform(
227
- self, input: Dict[str, Dict], **kwargs: Any
228
- ) -> Dict[str, Dict]:
231
+ self, input: dict[str, dict], **kwargs: Any
232
+ ) -> dict[str, dict]:
229
233
  """Fit and transform.
230
234
 
231
235
  Parameters
File without changes
@@ -1,4 +1,4 @@
1
- """Provide tests for marker collection."""
1
+ """Provide tests for MarkerCollection."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -12,10 +12,9 @@ from numpy.testing import assert_array_equal
12
12
  from junifer.datareader.default import DefaultDataReader
13
13
  from junifer.markers import (
14
14
  FunctionalConnectivityParcels,
15
- MarkerCollection,
16
15
  ParcelAggregation,
17
16
  )
18
- from junifer.pipeline import PipelineStepMixin
17
+ from junifer.pipeline import MarkerCollection, PipelineStepMixin
19
18
  from junifer.preprocess import fMRIPrepConfoundRemover
20
19
  from junifer.storage import SQLiteFeatureStorage
21
20
  from junifer.testing.datagrabbers import (