junifer 0.0.4.dev829__py3-none-any.whl → 0.0.5__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.
- junifer/__init__.py +17 -0
- junifer/_version.py +2 -2
- junifer/api/__init__.py +4 -1
- junifer/api/cli.py +91 -1
- junifer/api/decorators.py +9 -0
- junifer/api/functions.py +56 -10
- junifer/api/parser.py +3 -0
- junifer/api/queue_context/__init__.py +4 -1
- junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
- junifer/api/queue_context/htcondor_adapter.py +16 -5
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
- junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
- junifer/api/res/afni/run_afni_docker.sh +1 -1
- junifer/api/res/ants/run_ants_docker.sh +1 -1
- junifer/api/res/freesurfer/mri_binarize +3 -0
- junifer/api/res/freesurfer/mri_mc +3 -0
- junifer/api/res/freesurfer/mri_pretess +3 -0
- junifer/api/res/freesurfer/mris_convert +3 -0
- junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
- junifer/api/res/fsl/run_fsl_docker.sh +1 -1
- junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
- junifer/api/res/run_conda.zsh +23 -0
- junifer/api/res/run_venv.bash +22 -0
- junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
- junifer/api/tests/test_api_utils.py +4 -2
- junifer/api/tests/test_cli.py +83 -0
- junifer/api/tests/test_functions.py +27 -2
- junifer/configs/__init__.py +1 -1
- junifer/configs/juseless/__init__.py +4 -1
- junifer/configs/juseless/datagrabbers/__init__.py +10 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
- junifer/configs/juseless/datagrabbers/ucla.py +12 -9
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
- junifer/data/__init__.py +21 -1
- junifer/data/coordinates.py +10 -19
- junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
- junifer/data/masks.py +58 -87
- junifer/data/parcellations.py +14 -3
- junifer/data/template_spaces.py +4 -1
- junifer/data/tests/test_masks.py +26 -37
- junifer/data/utils.py +3 -0
- junifer/datagrabber/__init__.py +18 -1
- junifer/datagrabber/aomic/__init__.py +3 -0
- junifer/datagrabber/aomic/id1000.py +70 -37
- junifer/datagrabber/aomic/piop1.py +69 -36
- junifer/datagrabber/aomic/piop2.py +71 -38
- junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
- junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
- junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
- junifer/datagrabber/base.py +13 -6
- junifer/datagrabber/datalad_base.py +13 -1
- junifer/datagrabber/dmcc13_benchmark.py +36 -53
- junifer/datagrabber/hcp1200/__init__.py +3 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
- junifer/datagrabber/hcp1200/hcp1200.py +4 -1
- junifer/datagrabber/multiple.py +45 -6
- junifer/datagrabber/pattern.py +170 -62
- junifer/datagrabber/pattern_datalad.py +25 -12
- junifer/datagrabber/pattern_validation_mixin.py +388 -0
- junifer/datagrabber/tests/test_datalad_base.py +4 -4
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
- junifer/datagrabber/tests/test_multiple.py +161 -84
- junifer/datagrabber/tests/test_pattern.py +45 -0
- junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
- junifer/datareader/__init__.py +4 -1
- junifer/datareader/default.py +95 -43
- junifer/external/BrainPrint/brainprint/__init__.py +4 -0
- junifer/external/BrainPrint/brainprint/_version.py +3 -0
- junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
- junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
- junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
- junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
- junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
- junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
- junifer/external/__init__.py +1 -1
- junifer/external/nilearn/__init__.py +5 -1
- junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
- junifer/markers/__init__.py +23 -1
- junifer/markers/base.py +68 -28
- junifer/markers/brainprint.py +459 -0
- junifer/markers/collection.py +10 -2
- junifer/markers/complexity/__init__.py +10 -0
- junifer/markers/complexity/complexity_base.py +26 -43
- junifer/markers/complexity/hurst_exponent.py +3 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
- junifer/markers/complexity/perm_entropy.py +3 -0
- junifer/markers/complexity/range_entropy.py +3 -0
- junifer/markers/complexity/range_entropy_auc.py +3 -0
- junifer/markers/complexity/sample_entropy.py +3 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
- junifer/markers/complexity/weighted_perm_entropy.py +3 -0
- junifer/markers/ets_rss.py +27 -42
- junifer/markers/falff/__init__.py +3 -0
- junifer/markers/falff/_afni_falff.py +5 -2
- junifer/markers/falff/_junifer_falff.py +3 -0
- junifer/markers/falff/falff_base.py +20 -46
- junifer/markers/falff/falff_parcels.py +56 -27
- junifer/markers/falff/falff_spheres.py +60 -29
- junifer/markers/falff/tests/test_falff_parcels.py +39 -23
- junifer/markers/falff/tests/test_falff_spheres.py +39 -23
- junifer/markers/functional_connectivity/__init__.py +9 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
- junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
- junifer/markers/parcel_aggregation.py +47 -61
- junifer/markers/reho/__init__.py +3 -0
- junifer/markers/reho/_afni_reho.py +5 -2
- junifer/markers/reho/_junifer_reho.py +4 -1
- junifer/markers/reho/reho_base.py +8 -27
- junifer/markers/reho/reho_parcels.py +28 -17
- junifer/markers/reho/reho_spheres.py +27 -18
- junifer/markers/reho/tests/test_reho_parcels.py +8 -3
- junifer/markers/reho/tests/test_reho_spheres.py +8 -3
- junifer/markers/sphere_aggregation.py +43 -59
- junifer/markers/temporal_snr/__init__.py +3 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
- junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
- junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
- junifer/markers/tests/test_brainprint.py +58 -0
- junifer/markers/tests/test_collection.py +9 -8
- junifer/markers/tests/test_ets_rss.py +15 -9
- junifer/markers/tests/test_markers_base.py +17 -18
- junifer/markers/tests/test_parcel_aggregation.py +93 -32
- junifer/markers/tests/test_sphere_aggregation.py +72 -19
- junifer/onthefly/__init__.py +4 -1
- junifer/onthefly/read_transform.py +3 -0
- junifer/pipeline/__init__.py +9 -1
- junifer/pipeline/pipeline_step_mixin.py +21 -4
- junifer/pipeline/registry.py +3 -0
- junifer/pipeline/singleton.py +3 -0
- junifer/pipeline/tests/test_registry.py +1 -1
- junifer/pipeline/update_meta_mixin.py +3 -0
- junifer/pipeline/utils.py +67 -1
- junifer/pipeline/workdir_manager.py +3 -0
- junifer/preprocess/__init__.py +10 -2
- junifer/preprocess/base.py +6 -3
- junifer/preprocess/confounds/__init__.py +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
- junifer/preprocess/smoothing/__init__.py +9 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
- junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
- junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
- junifer/preprocess/smoothing/smoothing.py +174 -0
- junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
- junifer/preprocess/warping/__init__.py +3 -0
- junifer/preprocess/warping/_ants_warper.py +3 -0
- junifer/preprocess/warping/_fsl_warper.py +3 -0
- junifer/stats.py +4 -1
- junifer/storage/__init__.py +9 -1
- junifer/storage/base.py +40 -1
- junifer/storage/hdf5.py +71 -9
- junifer/storage/pandas_base.py +3 -0
- junifer/storage/sqlite.py +3 -0
- junifer/storage/tests/test_hdf5.py +82 -10
- junifer/storage/utils.py +9 -0
- junifer/testing/__init__.py +4 -1
- junifer/testing/datagrabbers.py +13 -6
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
- junifer/testing/utils.py +3 -0
- junifer/utils/__init__.py +13 -2
- junifer/utils/fs.py +3 -0
- junifer/utils/helpers.py +32 -1
- junifer/utils/logging.py +33 -4
- junifer/utils/tests/test_logging.py +8 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
- junifer-0.0.5.dist-info/RECORD +275 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
- junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
- junifer/datagrabber/utils.py +0 -230
- junifer/preprocess/ants/__init__.py +0 -4
- junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
- junifer/preprocess/bold_warper.py +0 -265
- junifer/preprocess/fsl/__init__.py +0 -4
- junifer/preprocess/fsl/apply_warper.py +0 -179
- junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
- junifer/preprocess/tests/test_bold_warper.py +0 -159
- junifer-0.0.4.dev829.dist-info/RECORD +0 -257
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
@@ -25,28 +25,26 @@ def test_MultipleDataGrabber() -> None:
|
|
25
25
|
repo_uri = _testing_dataset["example_bids_ses"]["uri"]
|
26
26
|
rootdir = "example_bids_ses"
|
27
27
|
replacements = ["subject", "session"]
|
28
|
-
|
29
|
-
"T1w": {
|
30
|
-
"pattern": (
|
31
|
-
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
32
|
-
),
|
33
|
-
"space": "native",
|
34
|
-
},
|
35
|
-
}
|
36
|
-
pattern2 = {
|
37
|
-
"BOLD": {
|
38
|
-
"pattern": (
|
39
|
-
"{subject}/{session}/func/"
|
40
|
-
"{subject}_{session}_task-rest_bold.nii.gz"
|
41
|
-
),
|
42
|
-
"space": "MNI152NLin6Asym",
|
43
|
-
},
|
44
|
-
}
|
28
|
+
|
45
29
|
dg1 = PatternDataladDataGrabber(
|
46
30
|
rootdir=rootdir,
|
47
31
|
uri=repo_uri,
|
48
32
|
types=["T1w"],
|
49
|
-
patterns=
|
33
|
+
patterns={
|
34
|
+
"T1w": {
|
35
|
+
"pattern": (
|
36
|
+
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
37
|
+
),
|
38
|
+
"space": "native",
|
39
|
+
"mask": {
|
40
|
+
"pattern": (
|
41
|
+
"{subject}/{session}/anat/{subject}_{session}_"
|
42
|
+
"brain_mask.nii.gz"
|
43
|
+
),
|
44
|
+
"space": "native",
|
45
|
+
},
|
46
|
+
},
|
47
|
+
},
|
50
48
|
replacements=replacements,
|
51
49
|
)
|
52
50
|
|
@@ -54,7 +52,22 @@ def test_MultipleDataGrabber() -> None:
|
|
54
52
|
rootdir=rootdir,
|
55
53
|
uri=repo_uri,
|
56
54
|
types=["BOLD"],
|
57
|
-
patterns=
|
55
|
+
patterns={
|
56
|
+
"BOLD": {
|
57
|
+
"pattern": (
|
58
|
+
"{subject}/{session}/func/"
|
59
|
+
"{subject}_{session}_task-rest_bold.nii.gz"
|
60
|
+
),
|
61
|
+
"space": "MNI152NLin6Asym",
|
62
|
+
"mask": {
|
63
|
+
"pattern": (
|
64
|
+
"{subject}/{session}/func/"
|
65
|
+
"{subject}_{session}_task-rest_brain_mask.nii.gz"
|
66
|
+
),
|
67
|
+
"space": "MNI152NLin6Asym",
|
68
|
+
},
|
69
|
+
},
|
70
|
+
},
|
58
71
|
replacements=replacements,
|
59
72
|
)
|
60
73
|
|
@@ -73,14 +86,17 @@ def test_MultipleDataGrabber() -> None:
|
|
73
86
|
with dg:
|
74
87
|
subs = list(dg)
|
75
88
|
assert set(subs) == set(expected_subs)
|
76
|
-
|
89
|
+
# Check data type
|
77
90
|
elem = dg[("sub-01", "ses-01")]
|
91
|
+
# Check data types
|
78
92
|
assert "T1w" in elem
|
79
93
|
assert "BOLD" in elem
|
94
|
+
# Check meta
|
80
95
|
assert "meta" in elem["BOLD"]
|
81
96
|
meta = elem["BOLD"]["meta"]["datagrabber"]
|
82
97
|
assert "class" in meta
|
83
98
|
assert meta["class"] == "MultipleDataGrabber"
|
99
|
+
# Check datagrabbers
|
84
100
|
assert "datagrabbers" in meta
|
85
101
|
assert len(meta["datagrabbers"]) == 2
|
86
102
|
assert meta["datagrabbers"][0]["class"] == "PatternDataladDataGrabber"
|
@@ -89,40 +105,37 @@ def test_MultipleDataGrabber() -> None:
|
|
89
105
|
|
90
106
|
def test_MultipleDataGrabber_no_intersection() -> None:
|
91
107
|
"""Test MultipleDataGrabber without intersection (0 elements)."""
|
92
|
-
repo_uri1 = _testing_dataset["example_bids"]["uri"]
|
93
|
-
repo_uri2 = _testing_dataset["example_bids_ses"]["uri"]
|
94
108
|
rootdir = "example_bids_ses"
|
95
109
|
replacements = ["subject", "session"]
|
96
|
-
|
97
|
-
"T1w": {
|
98
|
-
"pattern": (
|
99
|
-
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
100
|
-
),
|
101
|
-
"space": "native",
|
102
|
-
},
|
103
|
-
}
|
104
|
-
pattern2 = {
|
105
|
-
"BOLD": {
|
106
|
-
"pattern": (
|
107
|
-
"{subject}/{session}/func/"
|
108
|
-
"{subject}_{session}_task-rest_bold.nii.gz"
|
109
|
-
),
|
110
|
-
"space": "MNI152NLin6Asym",
|
111
|
-
},
|
112
|
-
}
|
110
|
+
|
113
111
|
dg1 = PatternDataladDataGrabber(
|
114
112
|
rootdir=rootdir,
|
115
|
-
uri=
|
113
|
+
uri=_testing_dataset["example_bids"]["uri"],
|
116
114
|
types=["T1w"],
|
117
|
-
patterns=
|
115
|
+
patterns={
|
116
|
+
"T1w": {
|
117
|
+
"pattern": (
|
118
|
+
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
119
|
+
),
|
120
|
+
"space": "native",
|
121
|
+
},
|
122
|
+
},
|
118
123
|
replacements=replacements,
|
119
124
|
)
|
120
125
|
|
121
126
|
dg2 = PatternDataladDataGrabber(
|
122
127
|
rootdir=rootdir,
|
123
|
-
uri=
|
128
|
+
uri=_testing_dataset["example_bids_ses"]["uri"],
|
124
129
|
types=["BOLD"],
|
125
|
-
patterns=
|
130
|
+
patterns={
|
131
|
+
"BOLD": {
|
132
|
+
"pattern": (
|
133
|
+
"{subject}/{session}/func/"
|
134
|
+
"{subject}_{session}_task-rest_bold.nii.gz"
|
135
|
+
),
|
136
|
+
"space": "MNI152NLin6Asym",
|
137
|
+
},
|
138
|
+
},
|
126
139
|
replacements=replacements,
|
127
140
|
)
|
128
141
|
|
@@ -135,23 +148,19 @@ def test_MultipleDataGrabber_no_intersection() -> None:
|
|
135
148
|
|
136
149
|
def test_MultipleDataGrabber_get_item() -> None:
|
137
150
|
"""Test MultipleDataGrabber get_item() error."""
|
138
|
-
repo_uri1 = _testing_dataset["example_bids"]["uri"]
|
139
|
-
rootdir = "example_bids_ses"
|
140
|
-
replacements = ["subject", "session"]
|
141
|
-
pattern1 = {
|
142
|
-
"T1w": {
|
143
|
-
"pattern": (
|
144
|
-
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
145
|
-
),
|
146
|
-
"space": "native",
|
147
|
-
},
|
148
|
-
}
|
149
151
|
dg1 = PatternDataladDataGrabber(
|
150
|
-
rootdir=
|
151
|
-
uri=
|
152
|
+
rootdir="example_bids_ses",
|
153
|
+
uri=_testing_dataset["example_bids"]["uri"],
|
152
154
|
types=["T1w"],
|
153
|
-
patterns=
|
154
|
-
|
155
|
+
patterns={
|
156
|
+
"T1w": {
|
157
|
+
"pattern": (
|
158
|
+
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
159
|
+
),
|
160
|
+
"space": "native",
|
161
|
+
},
|
162
|
+
},
|
163
|
+
replacements=["subject", "session"],
|
155
164
|
)
|
156
165
|
|
157
166
|
dg = MultipleDataGrabber([dg1])
|
@@ -161,43 +170,111 @@ def test_MultipleDataGrabber_get_item() -> None:
|
|
161
170
|
|
162
171
|
def test_MultipleDataGrabber_validation() -> None:
|
163
172
|
"""Test MultipleDataGrabber init validation."""
|
164
|
-
repo_uri1 = _testing_dataset["example_bids"]["uri"]
|
165
|
-
repo_uri2 = _testing_dataset["example_bids_ses"]["uri"]
|
166
173
|
rootdir = "example_bids_ses"
|
167
|
-
|
168
|
-
replacement2 = ["subject"]
|
169
|
-
pattern1 = {
|
170
|
-
"T1w": {
|
171
|
-
"pattern": (
|
172
|
-
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
173
|
-
),
|
174
|
-
"space": "native",
|
175
|
-
},
|
176
|
-
}
|
177
|
-
pattern2 = {
|
178
|
-
"BOLD": {
|
179
|
-
"pattern": "{subject}/func/{subject}_task-rest_bold.nii.gz",
|
180
|
-
"space": "MNI152NLin6Asym",
|
181
|
-
},
|
182
|
-
}
|
174
|
+
|
183
175
|
dg1 = PatternDataladDataGrabber(
|
184
176
|
rootdir=rootdir,
|
185
|
-
uri=
|
177
|
+
uri=_testing_dataset["example_bids"]["uri"],
|
186
178
|
types=["T1w"],
|
187
|
-
patterns=
|
188
|
-
|
179
|
+
patterns={
|
180
|
+
"T1w": {
|
181
|
+
"pattern": (
|
182
|
+
"{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
|
183
|
+
),
|
184
|
+
"space": "native",
|
185
|
+
},
|
186
|
+
},
|
187
|
+
replacements=["subject", "session"],
|
189
188
|
)
|
190
189
|
|
191
190
|
dg2 = PatternDataladDataGrabber(
|
192
191
|
rootdir=rootdir,
|
193
|
-
uri=
|
192
|
+
uri=_testing_dataset["example_bids_ses"]["uri"],
|
194
193
|
types=["BOLD"],
|
195
|
-
patterns=
|
196
|
-
|
194
|
+
patterns={
|
195
|
+
"BOLD": {
|
196
|
+
"pattern": "{subject}/func/{subject}_task-rest_bold.nii.gz",
|
197
|
+
"space": "MNI152NLin6Asym",
|
198
|
+
},
|
199
|
+
},
|
200
|
+
replacements=["subject"],
|
197
201
|
)
|
198
202
|
|
199
|
-
with pytest.raises(
|
203
|
+
with pytest.raises(RuntimeError, match="have different element keys"):
|
200
204
|
MultipleDataGrabber([dg1, dg2])
|
201
205
|
|
202
|
-
with pytest.raises(
|
206
|
+
with pytest.raises(RuntimeError, match="have overlapping mandatory"):
|
203
207
|
MultipleDataGrabber([dg1, dg1])
|
208
|
+
|
209
|
+
|
210
|
+
def test_MultipleDataGrabber_partial_pattern() -> None:
|
211
|
+
"""Test MultipleDataGrabber partial pattern."""
|
212
|
+
repo_uri = _testing_dataset["example_bids_ses"]["uri"]
|
213
|
+
rootdir = "example_bids_ses"
|
214
|
+
replacements = ["subject", "session"]
|
215
|
+
|
216
|
+
dg1 = PatternDataladDataGrabber(
|
217
|
+
rootdir=rootdir,
|
218
|
+
uri=repo_uri,
|
219
|
+
types=["BOLD"],
|
220
|
+
patterns={
|
221
|
+
"BOLD": {
|
222
|
+
"pattern": (
|
223
|
+
"{subject}/{session}/func/"
|
224
|
+
"{subject}_{session}_task-rest_bold.nii.gz"
|
225
|
+
),
|
226
|
+
"space": "MNI152NLin6Asym",
|
227
|
+
},
|
228
|
+
},
|
229
|
+
replacements=replacements,
|
230
|
+
)
|
231
|
+
|
232
|
+
dg2 = PatternDataladDataGrabber(
|
233
|
+
rootdir=rootdir,
|
234
|
+
uri=repo_uri,
|
235
|
+
types=["BOLD"],
|
236
|
+
patterns={
|
237
|
+
"BOLD": {
|
238
|
+
"confounds": {
|
239
|
+
"pattern": (
|
240
|
+
"{subject}/{session}/func/"
|
241
|
+
"{subject}_{session}_task-rest_"
|
242
|
+
"confounds_regressors.tsv"
|
243
|
+
),
|
244
|
+
"format": "fmriprep",
|
245
|
+
},
|
246
|
+
},
|
247
|
+
},
|
248
|
+
replacements=["subject", "session"],
|
249
|
+
partial_pattern_ok=True,
|
250
|
+
)
|
251
|
+
|
252
|
+
dg = MultipleDataGrabber([dg1, dg2])
|
253
|
+
|
254
|
+
types = dg.get_types()
|
255
|
+
assert "BOLD" in types
|
256
|
+
|
257
|
+
expected_subs = [
|
258
|
+
(f"sub-{i:02d}", f"ses-{j:02d}")
|
259
|
+
for j in range(1, 3)
|
260
|
+
for i in range(1, 10)
|
261
|
+
]
|
262
|
+
|
263
|
+
with dg:
|
264
|
+
subs = list(dg)
|
265
|
+
assert set(subs) == set(expected_subs)
|
266
|
+
# Fetch element
|
267
|
+
elem = dg[("sub-01", "ses-01")]
|
268
|
+
# Check data type and nested data type
|
269
|
+
assert "BOLD" in elem
|
270
|
+
assert "confounds" in elem["BOLD"]
|
271
|
+
# Check meta
|
272
|
+
assert "meta" in elem["BOLD"]
|
273
|
+
meta = elem["BOLD"]["meta"]["datagrabber"]
|
274
|
+
assert "class" in meta
|
275
|
+
assert meta["class"] == "MultipleDataGrabber"
|
276
|
+
# Check datagrabbers
|
277
|
+
assert "datagrabbers" in meta
|
278
|
+
assert len(meta["datagrabbers"]) == 2
|
279
|
+
assert meta["datagrabbers"][0]["class"] == "PatternDataladDataGrabber"
|
280
|
+
assert meta["datagrabbers"][1]["class"] == "PatternDataladDataGrabber"
|
@@ -236,6 +236,51 @@ def test_PatternDataGrabber(tmp_path: Path) -> None:
|
|
236
236
|
assert out1["VBM_GM"]["path"] != out2["VBM_GM"]["path"]
|
237
237
|
|
238
238
|
|
239
|
+
def test_PatternDataGrabber_unix_path_expansion(tmp_path: Path) -> None:
|
240
|
+
"""Test PatterDataGrabber for patterns with unix path expansion.
|
241
|
+
|
242
|
+
Parameters
|
243
|
+
----------
|
244
|
+
tmp_path : pathlib.Path
|
245
|
+
The path to the test directory.
|
246
|
+
|
247
|
+
"""
|
248
|
+
# Create test data root dir
|
249
|
+
freesurfer_dir = tmp_path / "derivatives" / "freesurfer"
|
250
|
+
freesurfer_dir.mkdir(parents=True, exist_ok=True)
|
251
|
+
# Create test data sub dirs and files
|
252
|
+
for dir_name in ["fsaverage", "sub-0001"]:
|
253
|
+
mri_dir = freesurfer_dir / dir_name / "mri"
|
254
|
+
mri_dir.mkdir(parents=True, exist_ok=True)
|
255
|
+
# Create files
|
256
|
+
(mri_dir / "T1.mgz").touch(exist_ok=True)
|
257
|
+
(mri_dir / "aseg.mgz").touch(exist_ok=True)
|
258
|
+
# Create datagrabber
|
259
|
+
dg = PatternDataGrabber(
|
260
|
+
datadir=tmp_path,
|
261
|
+
types=["FreeSurfer"],
|
262
|
+
patterns={
|
263
|
+
"FreeSurfer": {
|
264
|
+
"pattern": "derivatives/freesurfer/[!f]{subject}/mri/T1.mg[z]",
|
265
|
+
"aseg": {
|
266
|
+
"pattern": (
|
267
|
+
"derivatives/freesurfer/[!f]{subject}/mri/aseg.mg[z]"
|
268
|
+
)
|
269
|
+
},
|
270
|
+
},
|
271
|
+
},
|
272
|
+
replacements=["subject"],
|
273
|
+
)
|
274
|
+
# Check that "fsaverage" is filtered
|
275
|
+
elements = dg.get_elements()
|
276
|
+
assert elements == ["sub-0001"]
|
277
|
+
# Fetch data
|
278
|
+
out = dg["sub-0001"]
|
279
|
+
# Check paths are found
|
280
|
+
assert set(out["FreeSurfer"].keys()) == {"path", "aseg", "meta"}
|
281
|
+
assert list(out["FreeSurfer"]["aseg"].keys()) == ["path"]
|
282
|
+
|
283
|
+
|
239
284
|
def test_PatternDataGrabber_confounds_format_error_on_init() -> None:
|
240
285
|
"""Test PatterDataGrabber confounds format error on initialisation."""
|
241
286
|
with pytest.raises(
|
@@ -15,13 +15,13 @@ from junifer.datagrabber import PatternDataladDataGrabber
|
|
15
15
|
_testing_dataset = {
|
16
16
|
"example_bids": {
|
17
17
|
"uri": "https://gin.g-node.org/juaml/datalad-example-bids",
|
18
|
-
"commit": "
|
19
|
-
"id": "
|
18
|
+
"commit": "b87897cbe51bf0ee5514becaa5c7dd76491db5ad",
|
19
|
+
"id": "8fddff30-6993-420a-9d1e-b5b028c59468",
|
20
20
|
},
|
21
21
|
"example_bids_ses": {
|
22
22
|
"uri": "https://gin.g-node.org/juaml/datalad-example-bids-ses",
|
23
|
-
"commit": "
|
24
|
-
"id": "
|
23
|
+
"commit": "6b163aa98af76a9eac0272273c27e14127850181",
|
24
|
+
"id": "715c17cf-a1b9-42d6-9af8-9f74c1a4a724",
|
25
25
|
},
|
26
26
|
}
|
27
27
|
|
@@ -0,0 +1,249 @@
|
|
1
|
+
"""Provide tests for PatternValidationMixin."""
|
2
|
+
|
3
|
+
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
+
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
|
+
# License: AGPL
|
6
|
+
|
7
|
+
from contextlib import nullcontext
|
8
|
+
from typing import ContextManager, Dict, List, Union
|
9
|
+
|
10
|
+
import pytest
|
11
|
+
|
12
|
+
from junifer.datagrabber.pattern_validation_mixin import PatternValidationMixin
|
13
|
+
|
14
|
+
|
15
|
+
@pytest.mark.parametrize(
|
16
|
+
"types, replacements, patterns, expect",
|
17
|
+
[
|
18
|
+
(
|
19
|
+
"wrong",
|
20
|
+
[],
|
21
|
+
{},
|
22
|
+
pytest.raises(TypeError, match="`types` must be a list"),
|
23
|
+
),
|
24
|
+
(
|
25
|
+
[1],
|
26
|
+
[],
|
27
|
+
{},
|
28
|
+
pytest.raises(
|
29
|
+
TypeError, match="`types` must be a list of strings"
|
30
|
+
),
|
31
|
+
),
|
32
|
+
(
|
33
|
+
["BOLD"],
|
34
|
+
[],
|
35
|
+
"wrong",
|
36
|
+
pytest.raises(TypeError, match="`patterns` must be a dict"),
|
37
|
+
),
|
38
|
+
(
|
39
|
+
["T1w", "BOLD"],
|
40
|
+
"",
|
41
|
+
{
|
42
|
+
"T1w": {"pattern": "{subject}/anat/{subject}_T1w.nii.gz"},
|
43
|
+
},
|
44
|
+
pytest.raises(
|
45
|
+
ValueError,
|
46
|
+
match="Length of `types` more than that of `patterns`",
|
47
|
+
),
|
48
|
+
),
|
49
|
+
(
|
50
|
+
["T1w", "BOLD"],
|
51
|
+
"",
|
52
|
+
{
|
53
|
+
"T1w": {"pattern": "{subject}/anat/{subject}_T1w.nii.gz"},
|
54
|
+
"T2w": {"pattern": "{subject}/anat/{subject}_T2w.nii.gz"},
|
55
|
+
},
|
56
|
+
pytest.raises(
|
57
|
+
ValueError, match="`patterns` must contain all `types`"
|
58
|
+
),
|
59
|
+
),
|
60
|
+
(
|
61
|
+
["T3w"],
|
62
|
+
"",
|
63
|
+
{
|
64
|
+
"T3w": {"pattern": "{subject}/anat/{subject}_T3w.nii.gz"},
|
65
|
+
},
|
66
|
+
pytest.raises(ValueError, match="Unknown data type"),
|
67
|
+
),
|
68
|
+
(
|
69
|
+
["BOLD"],
|
70
|
+
"",
|
71
|
+
{
|
72
|
+
"BOLD": {"patterns": "{subject}/func/{subject}_BOLD.nii.gz"},
|
73
|
+
},
|
74
|
+
pytest.raises(KeyError, match="Mandatory key"),
|
75
|
+
),
|
76
|
+
(
|
77
|
+
["BOLD"],
|
78
|
+
"",
|
79
|
+
{
|
80
|
+
"BOLD": {
|
81
|
+
"pattern": (
|
82
|
+
"{subject}/func/{subject}_task-rest_bold.nii.gz"
|
83
|
+
),
|
84
|
+
"space": "MNINLin6Asym",
|
85
|
+
"confounds": {
|
86
|
+
"pattern": "{subject}/func/{subject}_confounds.tsv",
|
87
|
+
"format": "fmriprep",
|
88
|
+
},
|
89
|
+
"zip": "zap",
|
90
|
+
},
|
91
|
+
},
|
92
|
+
pytest.raises(RuntimeError, match="not accepted"),
|
93
|
+
),
|
94
|
+
(
|
95
|
+
["T1w"],
|
96
|
+
"",
|
97
|
+
{
|
98
|
+
"T1w": {
|
99
|
+
"pattern": "{subject}/anat/{subject}*.nii",
|
100
|
+
"space": "native",
|
101
|
+
},
|
102
|
+
},
|
103
|
+
pytest.raises(ValueError, match="following a replacement"),
|
104
|
+
),
|
105
|
+
(
|
106
|
+
["T1w"],
|
107
|
+
"wrong",
|
108
|
+
{
|
109
|
+
"T1w": {
|
110
|
+
"pattern": "{subject}/anat/{subject}_T1w.nii",
|
111
|
+
"space": "native",
|
112
|
+
},
|
113
|
+
},
|
114
|
+
pytest.raises(TypeError, match="`replacements` must be a list"),
|
115
|
+
),
|
116
|
+
(
|
117
|
+
["T1w"],
|
118
|
+
[1],
|
119
|
+
{
|
120
|
+
"T1w": {
|
121
|
+
"pattern": "{subject}/anat/{subject}_T1w.nii",
|
122
|
+
"space": "native",
|
123
|
+
},
|
124
|
+
},
|
125
|
+
pytest.raises(
|
126
|
+
TypeError, match="`replacements` must be a list of strings"
|
127
|
+
),
|
128
|
+
),
|
129
|
+
(
|
130
|
+
["T1w", "BOLD"],
|
131
|
+
["subject", "session"],
|
132
|
+
{
|
133
|
+
"T1w": {
|
134
|
+
"pattern": "{subject}/anat/{subject}_T1w.nii.gz",
|
135
|
+
"space": "native",
|
136
|
+
},
|
137
|
+
"BOLD": {
|
138
|
+
"pattern": (
|
139
|
+
"{subject}/func/{subject}_task-rest_bold.nii.gz"
|
140
|
+
),
|
141
|
+
"space": "MNI152NLin6Asym",
|
142
|
+
},
|
143
|
+
},
|
144
|
+
pytest.raises(ValueError, match="is not part of any pattern"),
|
145
|
+
),
|
146
|
+
(
|
147
|
+
["BOLD"],
|
148
|
+
["subject", "session"],
|
149
|
+
{
|
150
|
+
"T1w": {
|
151
|
+
"pattern": "{subject}/anat/_T1w.nii.gz",
|
152
|
+
"space": "native",
|
153
|
+
},
|
154
|
+
"BOLD": {
|
155
|
+
"pattern": "{session}/func/_task-rest_bold.nii.gz",
|
156
|
+
"space": "MNI152NLin6Asym",
|
157
|
+
},
|
158
|
+
},
|
159
|
+
pytest.raises(ValueError, match="At least one pattern"),
|
160
|
+
),
|
161
|
+
(
|
162
|
+
["T1w", "T2w", "BOLD"],
|
163
|
+
["subject"],
|
164
|
+
{
|
165
|
+
"T1w": {
|
166
|
+
"pattern": "{subject}/anat/{subject}_T1w.nii.gz",
|
167
|
+
"space": "native",
|
168
|
+
},
|
169
|
+
"T2w": {
|
170
|
+
"pattern": "{subject}/anat/{subject}_T2w.nii.gz",
|
171
|
+
"space": "native",
|
172
|
+
},
|
173
|
+
"BOLD": {
|
174
|
+
"pattern": (
|
175
|
+
"{subject}/func/{session}/{subject}_task-rest_bold.nii.gz"
|
176
|
+
),
|
177
|
+
"space": "MNI152NLin6Asym",
|
178
|
+
"confounds": {
|
179
|
+
"pattern": "{subject}/func/{subject}_confounds.tsv",
|
180
|
+
"format": "fmriprep",
|
181
|
+
},
|
182
|
+
},
|
183
|
+
},
|
184
|
+
nullcontext(),
|
185
|
+
),
|
186
|
+
],
|
187
|
+
)
|
188
|
+
def test_PatternValidationMixin(
|
189
|
+
types: Union[str, List[str], List[int]],
|
190
|
+
replacements: Union[str, List[str], List[int]],
|
191
|
+
patterns: Union[str, Dict[str, Dict[str, str]]],
|
192
|
+
expect: ContextManager,
|
193
|
+
) -> None:
|
194
|
+
"""Test validation.
|
195
|
+
|
196
|
+
Parameters
|
197
|
+
----------
|
198
|
+
types : str, list of int or str
|
199
|
+
The parametrized data types to validate.
|
200
|
+
replacements : str, list of str or int
|
201
|
+
The parametrized pattern replacements to validate.
|
202
|
+
patterns : str, dict
|
203
|
+
The parametrized patterns to validate against.
|
204
|
+
expect : typing.ContextManager
|
205
|
+
The parametrized ContextManager object.
|
206
|
+
|
207
|
+
"""
|
208
|
+
|
209
|
+
class MockDataGrabber(PatternValidationMixin):
|
210
|
+
def __init__(
|
211
|
+
self,
|
212
|
+
types,
|
213
|
+
replacements,
|
214
|
+
patterns,
|
215
|
+
) -> None:
|
216
|
+
self.types = types
|
217
|
+
self.replacements = replacements
|
218
|
+
self.patterns = patterns
|
219
|
+
|
220
|
+
def validate(self) -> None:
|
221
|
+
self.validate_patterns(
|
222
|
+
types=self.types,
|
223
|
+
replacements=self.replacements,
|
224
|
+
patterns=self.patterns,
|
225
|
+
)
|
226
|
+
|
227
|
+
dg = MockDataGrabber(types, replacements, patterns)
|
228
|
+
with expect:
|
229
|
+
dg.validate()
|
230
|
+
|
231
|
+
|
232
|
+
# This test is kept separate as bool doesn't support context manager protocol,
|
233
|
+
# used in the earlier test
|
234
|
+
def test_PatternValidationMixin_partial_pattern_check() -> None:
|
235
|
+
"""Test validation for partial patterns."""
|
236
|
+
with pytest.warns(RuntimeWarning, match="might not work as expected"):
|
237
|
+
PatternValidationMixin().validate_patterns(
|
238
|
+
types=["BOLD"],
|
239
|
+
replacements=["subject"],
|
240
|
+
patterns={
|
241
|
+
"BOLD": {
|
242
|
+
"mask": {
|
243
|
+
"pattern": "{subject}/func/{subject}_BOLD.nii.gz",
|
244
|
+
"space": "MNI152NLin6Asym",
|
245
|
+
},
|
246
|
+
},
|
247
|
+
}, # type: ignore
|
248
|
+
partial_pattern_ok=True,
|
249
|
+
)
|
junifer/datareader/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""DataReaders for datasets' data loading."""
|
2
2
|
|
3
3
|
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
4
|
# Leonard Sasse <l.sasse@fz-juelich.de>
|
@@ -6,3 +6,6 @@
|
|
6
6
|
# License: AGPL
|
7
7
|
|
8
8
|
from .default import DefaultDataReader
|
9
|
+
|
10
|
+
|
11
|
+
__all__ = ["DefaultDataReader"]
|