junifer 0.0.3.dev131__py3-none-any.whl → 0.0.3.dev148__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 +1 -0
- junifer/_version.py +2 -2
- junifer/data/parcellations.py +211 -1
- junifer/data/tests/test_parcellations.py +199 -0
- junifer/onthefly/__init__.py +6 -0
- junifer/onthefly/read_transform.py +134 -0
- junifer/onthefly/tests/test_read_transform.py +179 -0
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/METADATA +5 -1
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/RECORD +14 -11
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/WHEEL +0 -0
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev131.dist-info → junifer-0.0.3.dev148.dist-info}/top_level.txt +0 -0
junifer/__init__.py
CHANGED
junifer/_version.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
# file generated by setuptools_scm
|
2
2
|
# don't change, don't track in version control
|
3
|
-
__version__ = version = '0.0.3.
|
4
|
-
__version_tuple__ = version_tuple = (0, 0, 3, '
|
3
|
+
__version__ = version = '0.0.3.dev148'
|
4
|
+
__version_tuple__ = version_tuple = (0, 0, 3, 'dev148')
|
junifer/data/parcellations.py
CHANGED
@@ -103,6 +103,21 @@ for year in (2013, 2015, 2019):
|
|
103
103
|
"year": 2019,
|
104
104
|
"n_rois": 368,
|
105
105
|
}
|
106
|
+
# Add Yan parcellation info
|
107
|
+
for n_rois in range(100, 1001, 100):
|
108
|
+
# Add Yeo networks
|
109
|
+
for yeo_network in [7, 17]:
|
110
|
+
_available_parcellations[f"Yan{n_rois}xYeo{yeo_network}"] = {
|
111
|
+
"family": "Yan",
|
112
|
+
"n_rois": n_rois,
|
113
|
+
"yeo_networks": yeo_network,
|
114
|
+
}
|
115
|
+
# Add Kong networks
|
116
|
+
_available_parcellations[f"Yan{n_rois}xKong17"] = {
|
117
|
+
"family": "Yan",
|
118
|
+
"n_rois": n_rois,
|
119
|
+
"kong_networks": 17,
|
120
|
+
}
|
106
121
|
|
107
122
|
|
108
123
|
def register_parcellation(
|
@@ -273,7 +288,7 @@ def _retrieve_parcellation(
|
|
273
288
|
|
274
289
|
Parameters
|
275
290
|
----------
|
276
|
-
family : {"Schaefer", "SUIT", "Tian", "AICHA", "Shen"}
|
291
|
+
family : {"Schaefer", "SUIT", "Tian", "AICHA", "Shen", "Yan"}
|
277
292
|
The name of the parcellation family.
|
278
293
|
parcellations_dir : str or pathlib.Path, optional
|
279
294
|
Path where the retrieved parcellations file are stored. The default
|
@@ -316,6 +331,13 @@ def _retrieve_parcellation(
|
|
316
331
|
Number of ROIs to use. Can be ``50, 100, or 150`` for
|
317
332
|
``year = 2013`` but is fixed at ``268`` for ``year = 2015`` and at
|
318
333
|
``368`` for ``year = 2019``.
|
334
|
+
* Yan :
|
335
|
+
``n_rois`` : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}
|
336
|
+
Granularity of the parcellation to be used.
|
337
|
+
``yeo_networks`` : {7, 17}, optional
|
338
|
+
Number of Yeo networks to use (default None).
|
339
|
+
``kong_networks`` : {17}, optional
|
340
|
+
Number of Kong networks to use (default None).
|
319
341
|
|
320
342
|
Returns
|
321
343
|
-------
|
@@ -373,6 +395,12 @@ def _retrieve_parcellation(
|
|
373
395
|
resolution=resolution,
|
374
396
|
**kwargs,
|
375
397
|
)
|
398
|
+
elif family == "Yan":
|
399
|
+
parcellation_fname, parcellation_labels = _retrieve_yan(
|
400
|
+
parcellations_dir=parcellations_dir,
|
401
|
+
resolution=resolution,
|
402
|
+
**kwargs,
|
403
|
+
)
|
376
404
|
else:
|
377
405
|
raise_error(
|
378
406
|
f"The provided parcellation name {family} cannot be retrieved."
|
@@ -1089,6 +1117,188 @@ def _retrieve_shen( # noqa: C901
|
|
1089
1117
|
return parcellation_fname, labels
|
1090
1118
|
|
1091
1119
|
|
1120
|
+
def _retrieve_yan(
|
1121
|
+
parcellations_dir: Path,
|
1122
|
+
resolution: Optional[float] = None,
|
1123
|
+
n_rois: Optional[int] = None,
|
1124
|
+
yeo_networks: Optional[int] = None,
|
1125
|
+
kong_networks: Optional[int] = None,
|
1126
|
+
) -> Tuple[Path, List[str]]:
|
1127
|
+
"""Retrieve Yan parcellation.
|
1128
|
+
|
1129
|
+
Parameters
|
1130
|
+
----------
|
1131
|
+
parcellations_dir : pathlib.Path
|
1132
|
+
The path to the parcellation data directory.
|
1133
|
+
resolution : float, optional
|
1134
|
+
The desired resolution of the parcellation to load. If it is not
|
1135
|
+
available, the closest resolution will be loaded. Preferably, use a
|
1136
|
+
resolution higher than the desired one. By default, will load the
|
1137
|
+
highest one (default None). Available resolutions for this
|
1138
|
+
parcellation are 1mm and 2mm.
|
1139
|
+
n_rois : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}, optional
|
1140
|
+
Granularity of the parcellation to be used (default None).
|
1141
|
+
yeo_networks : {7, 17}, optional
|
1142
|
+
Number of Yeo networks to use (default None).
|
1143
|
+
kong_networks : {17}, optional
|
1144
|
+
Number of Kong networks to use (default None).
|
1145
|
+
|
1146
|
+
Returns
|
1147
|
+
-------
|
1148
|
+
pathlib.Path
|
1149
|
+
File path to the parcellation image.
|
1150
|
+
list of str
|
1151
|
+
Parcellation labels.
|
1152
|
+
|
1153
|
+
Raises
|
1154
|
+
------
|
1155
|
+
ValueError
|
1156
|
+
If invalid value is provided for ``n_rois``, ``yeo_networks`` or
|
1157
|
+
``kong_networks`` or if there is a problem fetching the parcellation.
|
1158
|
+
|
1159
|
+
"""
|
1160
|
+
logger.info("Parcellation parameters:")
|
1161
|
+
logger.info(f"\tresolution: {resolution}")
|
1162
|
+
logger.info(f"\tn_rois: {n_rois}")
|
1163
|
+
logger.info(f"\tyeo_networks: {yeo_networks}")
|
1164
|
+
logger.info(f"\tkong_networks: {kong_networks}")
|
1165
|
+
|
1166
|
+
# Allow single network type
|
1167
|
+
if (not yeo_networks and not kong_networks) or (
|
1168
|
+
yeo_networks and kong_networks
|
1169
|
+
):
|
1170
|
+
raise_error(
|
1171
|
+
"Either one of `yeo_networks` or `kong_networks` need to be "
|
1172
|
+
"specified."
|
1173
|
+
)
|
1174
|
+
|
1175
|
+
# Check resolution
|
1176
|
+
_valid_resolutions = [1, 2]
|
1177
|
+
resolution = closest_resolution(resolution, _valid_resolutions)
|
1178
|
+
|
1179
|
+
# Check n_rois value
|
1180
|
+
_valid_n_rois = list(range(100, 1001, 100))
|
1181
|
+
if n_rois not in _valid_n_rois:
|
1182
|
+
raise_error(
|
1183
|
+
f"The parameter `n_rois` ({n_rois}) needs to be one of the "
|
1184
|
+
f"following: {_valid_n_rois}"
|
1185
|
+
)
|
1186
|
+
|
1187
|
+
if yeo_networks:
|
1188
|
+
# Check yeo_networks value
|
1189
|
+
_valid_yeo_networks = [7, 17]
|
1190
|
+
if yeo_networks not in _valid_yeo_networks:
|
1191
|
+
raise_error(
|
1192
|
+
f"The parameter `yeo_networks` ({yeo_networks}) needs to be "
|
1193
|
+
f"one of the following: {_valid_yeo_networks}"
|
1194
|
+
)
|
1195
|
+
# Define image and label file according to network
|
1196
|
+
parcellation_fname = (
|
1197
|
+
parcellations_dir
|
1198
|
+
/ "Yan_2023"
|
1199
|
+
/ (
|
1200
|
+
f"{n_rois}Parcels_Yeo2011_{yeo_networks}Networks_FSLMNI152_"
|
1201
|
+
f"{resolution}mm.nii.gz"
|
1202
|
+
)
|
1203
|
+
)
|
1204
|
+
parcellation_lname = (
|
1205
|
+
parcellations_dir
|
1206
|
+
/ "Yan_2023"
|
1207
|
+
/ f"{n_rois}Parcels_Yeo2011_{yeo_networks}Networks_LUT.txt"
|
1208
|
+
)
|
1209
|
+
elif kong_networks:
|
1210
|
+
# Check kong_networks value
|
1211
|
+
_valid_kong_networks = [17]
|
1212
|
+
if kong_networks not in _valid_kong_networks:
|
1213
|
+
raise_error(
|
1214
|
+
f"The parameter `kong_networks` ({kong_networks}) needs to be "
|
1215
|
+
f"one of the following: {_valid_kong_networks}"
|
1216
|
+
)
|
1217
|
+
# Define image and label file according to network
|
1218
|
+
parcellation_fname = (
|
1219
|
+
parcellations_dir
|
1220
|
+
/ "Yan_2023"
|
1221
|
+
/ (
|
1222
|
+
f"{n_rois}Parcels_Kong2022_{kong_networks}Networks_FSLMNI152_"
|
1223
|
+
f"{resolution}mm.nii.gz"
|
1224
|
+
)
|
1225
|
+
)
|
1226
|
+
parcellation_lname = (
|
1227
|
+
parcellations_dir
|
1228
|
+
/ "Yan_2023"
|
1229
|
+
/ f"{n_rois}Parcels_Kong2022_{kong_networks}Networks_LUT.txt"
|
1230
|
+
)
|
1231
|
+
|
1232
|
+
# Check for existence of parcellation:
|
1233
|
+
if not parcellation_fname.exists() and not parcellation_lname.exists():
|
1234
|
+
logger.info(
|
1235
|
+
"At least one of the parcellation files are missing, fetching."
|
1236
|
+
)
|
1237
|
+
|
1238
|
+
# Set URL based on network
|
1239
|
+
if yeo_networks:
|
1240
|
+
img_url = (
|
1241
|
+
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/"
|
1242
|
+
"master/stable_projects/brain_parcellation/Yan2023_homotopic/"
|
1243
|
+
f"parcellations/MNI/yeo{yeo_networks}/{n_rois}Parcels_Yeo2011"
|
1244
|
+
f"_{yeo_networks}Networks_FSLMNI152_{resolution}mm.nii.gz"
|
1245
|
+
)
|
1246
|
+
label_url = (
|
1247
|
+
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/"
|
1248
|
+
"master/stable_projects/brain_parcellation/Yan2023_homotopic/"
|
1249
|
+
f"parcellations/MNI/yeo{yeo_networks}/freeview_lut/{n_rois}"
|
1250
|
+
f"Parcels_Yeo2011_{yeo_networks}Networks_LUT.txt"
|
1251
|
+
)
|
1252
|
+
elif kong_networks:
|
1253
|
+
img_url = (
|
1254
|
+
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/"
|
1255
|
+
"master/stable_projects/brain_parcellation/Yan2023_homotopic/"
|
1256
|
+
f"parcellations/MNI/kong17/{n_rois}Parcels_Kong2022"
|
1257
|
+
f"_17Networks_FSLMNI152_{resolution}mm.nii.gz"
|
1258
|
+
)
|
1259
|
+
label_url = (
|
1260
|
+
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/"
|
1261
|
+
"master/stable_projects/brain_parcellation/Yan2023_homotopic/"
|
1262
|
+
f"parcellations/MNI/kong17/freeview_lut/{n_rois}Parcels_"
|
1263
|
+
"Kong2022_17Networks_LUT.txt"
|
1264
|
+
)
|
1265
|
+
|
1266
|
+
# Initiate a session and make HTTP requests
|
1267
|
+
session = requests.Session()
|
1268
|
+
# Download parcellation file
|
1269
|
+
logger.info(f"Downloading Yan 2023 parcellation from {img_url}")
|
1270
|
+
try:
|
1271
|
+
img_resp = session.get(img_url)
|
1272
|
+
img_resp.raise_for_status()
|
1273
|
+
except (ConnectionError, ReadTimeout, HTTPError) as err:
|
1274
|
+
raise_error(
|
1275
|
+
f"Failed to download Yan 2023 parcellation due to: {err}"
|
1276
|
+
)
|
1277
|
+
else:
|
1278
|
+
parcellation_img_path = Path(parcellation_fname)
|
1279
|
+
parcellation_img_path.parent.mkdir(parents=True, exist_ok=True)
|
1280
|
+
parcellation_img_path.touch(exist_ok=True)
|
1281
|
+
with open(parcellation_img_path, "wb") as f:
|
1282
|
+
f.write(img_resp.content)
|
1283
|
+
# Download label file
|
1284
|
+
logger.info(f"Downloading Yan 2023 labels from {label_url}")
|
1285
|
+
try:
|
1286
|
+
label_resp = session.get(label_url)
|
1287
|
+
label_resp.raise_for_status()
|
1288
|
+
except (ConnectionError, ReadTimeout, HTTPError) as err:
|
1289
|
+
raise_error(f"Failed to download Yan 2023 labels due to: {err}")
|
1290
|
+
else:
|
1291
|
+
parcellation_labels_path = Path(parcellation_lname)
|
1292
|
+
parcellation_labels_path.touch(exist_ok=True)
|
1293
|
+
with open(parcellation_labels_path, "wb") as f:
|
1294
|
+
f.write(label_resp.content)
|
1295
|
+
|
1296
|
+
# Load label file
|
1297
|
+
labels = pd.read_csv(parcellation_lname, sep=" ", header=None)[1].to_list()
|
1298
|
+
|
1299
|
+
return parcellation_fname, labels
|
1300
|
+
|
1301
|
+
|
1092
1302
|
def merge_parcellations(
|
1093
1303
|
parcellations_list: List["Nifti1Image"],
|
1094
1304
|
parcellations_names: List[str],
|
@@ -21,6 +21,7 @@ from junifer.data.parcellations import (
|
|
21
21
|
_retrieve_shen,
|
22
22
|
_retrieve_suit,
|
23
23
|
_retrieve_tian,
|
24
|
+
_retrieve_yan,
|
24
25
|
list_parcellations,
|
25
26
|
load_parcellation,
|
26
27
|
merge_parcellations,
|
@@ -728,6 +729,204 @@ def test_retrieve_shen_incorrect_param_combo(
|
|
728
729
|
)
|
729
730
|
|
730
731
|
|
732
|
+
@pytest.mark.parametrize(
|
733
|
+
"resolution, n_rois, yeo_networks, kong_networks",
|
734
|
+
[
|
735
|
+
(1.0, 100, 7, None),
|
736
|
+
(1.0, 200, 7, None),
|
737
|
+
(1.0, 300, 7, None),
|
738
|
+
(1.0, 400, 7, None),
|
739
|
+
(1.0, 500, 7, None),
|
740
|
+
(1.0, 600, 7, None),
|
741
|
+
(1.0, 700, 7, None),
|
742
|
+
(1.0, 800, 7, None),
|
743
|
+
(1.0, 900, 7, None),
|
744
|
+
(1.0, 1000, 7, None),
|
745
|
+
(2.0, 100, 7, None),
|
746
|
+
(2.0, 200, 7, None),
|
747
|
+
(2.0, 300, 7, None),
|
748
|
+
(2.0, 400, 7, None),
|
749
|
+
(2.0, 500, 7, None),
|
750
|
+
(2.0, 600, 7, None),
|
751
|
+
(2.0, 700, 7, None),
|
752
|
+
(2.0, 800, 7, None),
|
753
|
+
(2.0, 900, 7, None),
|
754
|
+
(2.0, 1000, 7, None),
|
755
|
+
(1.0, 100, 17, None),
|
756
|
+
(1.0, 200, 17, None),
|
757
|
+
(1.0, 300, 17, None),
|
758
|
+
(1.0, 400, 17, None),
|
759
|
+
(1.0, 500, 17, None),
|
760
|
+
(1.0, 600, 17, None),
|
761
|
+
(1.0, 700, 17, None),
|
762
|
+
(1.0, 800, 17, None),
|
763
|
+
(1.0, 900, 17, None),
|
764
|
+
(1.0, 1000, 17, None),
|
765
|
+
(2.0, 100, 17, None),
|
766
|
+
(2.0, 200, 17, None),
|
767
|
+
(2.0, 300, 17, None),
|
768
|
+
(2.0, 400, 17, None),
|
769
|
+
(2.0, 500, 17, None),
|
770
|
+
(2.0, 600, 17, None),
|
771
|
+
(2.0, 700, 17, None),
|
772
|
+
(2.0, 800, 17, None),
|
773
|
+
(2.0, 900, 17, None),
|
774
|
+
(2.0, 1000, 17, None),
|
775
|
+
(1.0, 100, None, 17),
|
776
|
+
(1.0, 200, None, 17),
|
777
|
+
(1.0, 300, None, 17),
|
778
|
+
(1.0, 400, None, 17),
|
779
|
+
(1.0, 500, None, 17),
|
780
|
+
(1.0, 600, None, 17),
|
781
|
+
(1.0, 700, None, 17),
|
782
|
+
(1.0, 800, None, 17),
|
783
|
+
(1.0, 900, None, 17),
|
784
|
+
(1.0, 1000, None, 17),
|
785
|
+
(2.0, 100, None, 17),
|
786
|
+
(2.0, 200, None, 17),
|
787
|
+
(2.0, 300, None, 17),
|
788
|
+
(2.0, 400, None, 17),
|
789
|
+
(2.0, 500, None, 17),
|
790
|
+
(2.0, 600, None, 17),
|
791
|
+
(2.0, 700, None, 17),
|
792
|
+
(2.0, 800, None, 17),
|
793
|
+
(2.0, 900, None, 17),
|
794
|
+
(2.0, 1000, None, 17),
|
795
|
+
],
|
796
|
+
)
|
797
|
+
def test_yan(
|
798
|
+
tmp_path: Path,
|
799
|
+
resolution: float,
|
800
|
+
n_rois: int,
|
801
|
+
yeo_networks: int,
|
802
|
+
kong_networks: int,
|
803
|
+
) -> None:
|
804
|
+
"""Test Yan parcellation.
|
805
|
+
|
806
|
+
Parameters
|
807
|
+
----------
|
808
|
+
tmp_path : pathlib.Path
|
809
|
+
The path to the test directory.
|
810
|
+
resolution : float
|
811
|
+
The parametrized resolution values.
|
812
|
+
n_rois : int
|
813
|
+
The parametrized ROI count values.
|
814
|
+
yeo_networks : int
|
815
|
+
The parametrized Yeo networks values.
|
816
|
+
kong_networks : int
|
817
|
+
The parametrized Kong networks values.
|
818
|
+
|
819
|
+
"""
|
820
|
+
parcellations = list_parcellations()
|
821
|
+
if yeo_networks:
|
822
|
+
parcellation_name = f"Yan{n_rois}xYeo{yeo_networks}"
|
823
|
+
assert parcellation_name in parcellations
|
824
|
+
parcellation_file = (
|
825
|
+
f"{n_rois}Parcels_Yeo2011_{yeo_networks}Networks_FSLMNI152_"
|
826
|
+
f"{int(resolution)}mm.nii.gz"
|
827
|
+
)
|
828
|
+
elif kong_networks:
|
829
|
+
parcellation_name = f"Yan{n_rois}xKong{kong_networks}"
|
830
|
+
assert parcellation_name in parcellations
|
831
|
+
parcellation_file = (
|
832
|
+
f"{n_rois}Parcels_Kong2022_{kong_networks}Networks_FSLMNI152_"
|
833
|
+
f"{int(resolution)}mm.nii.gz"
|
834
|
+
)
|
835
|
+
# Load parcellation
|
836
|
+
img, label, img_path = load_parcellation(
|
837
|
+
name=parcellation_name, # type: ignore
|
838
|
+
parcellations_dir=tmp_path,
|
839
|
+
resolution=resolution,
|
840
|
+
)
|
841
|
+
assert img is not None
|
842
|
+
assert img_path.name == parcellation_file # type: ignore
|
843
|
+
assert len(label) == n_rois
|
844
|
+
assert_array_equal(
|
845
|
+
img.header["pixdim"][1:4], 3 * [resolution] # type: ignore
|
846
|
+
)
|
847
|
+
|
848
|
+
|
849
|
+
def test_retrieve_yan_incorrect_networks(tmp_path: Path) -> None:
|
850
|
+
"""Test retrieve Yan with incorrect networks.
|
851
|
+
|
852
|
+
Parameters
|
853
|
+
----------
|
854
|
+
tmp_path : pathlib.Path
|
855
|
+
The path to the test directory.
|
856
|
+
|
857
|
+
"""
|
858
|
+
with pytest.raises(
|
859
|
+
ValueError, match="Either one of `yeo_networks` or `kong_networks`"
|
860
|
+
):
|
861
|
+
_retrieve_yan(
|
862
|
+
parcellations_dir=tmp_path,
|
863
|
+
n_rois=31418,
|
864
|
+
yeo_networks=100,
|
865
|
+
kong_networks=100,
|
866
|
+
)
|
867
|
+
|
868
|
+
with pytest.raises(
|
869
|
+
ValueError, match="Either one of `yeo_networks` or `kong_networks`"
|
870
|
+
):
|
871
|
+
_retrieve_yan(
|
872
|
+
parcellations_dir=tmp_path,
|
873
|
+
n_rois=31418,
|
874
|
+
yeo_networks=None,
|
875
|
+
kong_networks=None,
|
876
|
+
)
|
877
|
+
|
878
|
+
|
879
|
+
def test_retrieve_yan_incorrect_n_rois(tmp_path: Path) -> None:
|
880
|
+
"""Test retrieve Yan with incorrect ROIs.
|
881
|
+
|
882
|
+
Parameters
|
883
|
+
----------
|
884
|
+
tmp_path : pathlib.Path
|
885
|
+
The path to the test directory.
|
886
|
+
|
887
|
+
"""
|
888
|
+
with pytest.raises(ValueError, match="The parameter `n_rois`"):
|
889
|
+
_retrieve_yan(
|
890
|
+
parcellations_dir=tmp_path,
|
891
|
+
n_rois=31418,
|
892
|
+
yeo_networks=7,
|
893
|
+
)
|
894
|
+
|
895
|
+
|
896
|
+
def test_retrieve_yan_incorrect_yeo_networks(tmp_path: Path) -> None:
|
897
|
+
"""Test retrieve Yan with incorrect Yeo networks.
|
898
|
+
|
899
|
+
Parameters
|
900
|
+
----------
|
901
|
+
tmp_path : pathlib.Path
|
902
|
+
The path to the test directory.
|
903
|
+
|
904
|
+
"""
|
905
|
+
with pytest.raises(ValueError, match="The parameter `yeo_networks`"):
|
906
|
+
_retrieve_yan(
|
907
|
+
parcellations_dir=tmp_path,
|
908
|
+
n_rois=100,
|
909
|
+
yeo_networks=27,
|
910
|
+
)
|
911
|
+
|
912
|
+
|
913
|
+
def test_retrieve_yan_incorrect_kong_networks(tmp_path: Path) -> None:
|
914
|
+
"""Test retrieve Yan with incorrect Kong networks.
|
915
|
+
|
916
|
+
Parameters
|
917
|
+
----------
|
918
|
+
tmp_path : pathlib.Path
|
919
|
+
The path to the test directory.
|
920
|
+
|
921
|
+
"""
|
922
|
+
with pytest.raises(ValueError, match="The parameter `kong_networks`"):
|
923
|
+
_retrieve_yan(
|
924
|
+
parcellations_dir=tmp_path,
|
925
|
+
n_rois=100,
|
926
|
+
kong_networks=27,
|
927
|
+
)
|
928
|
+
|
929
|
+
|
731
930
|
def test_merge_parcellations() -> None:
|
732
931
|
"""Test merging parcellations."""
|
733
932
|
# load some parcellations for testing
|
@@ -0,0 +1,134 @@
|
|
1
|
+
"""Provide implementation for read-and-transform function."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
|
7
|
+
from typing import TYPE_CHECKING, Dict, Optional, Tuple, Type
|
8
|
+
|
9
|
+
import pandas as pd
|
10
|
+
|
11
|
+
from ..utils import logger, raise_error, warn_with_log
|
12
|
+
|
13
|
+
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from junifer.storage import BaseFeatureStorage
|
16
|
+
|
17
|
+
|
18
|
+
def read_transform(
|
19
|
+
storage: Type["BaseFeatureStorage"],
|
20
|
+
feature_name: str,
|
21
|
+
transform: str,
|
22
|
+
transform_args: Optional[Tuple] = None,
|
23
|
+
transform_kw_args: Optional[Dict] = None,
|
24
|
+
) -> pd.DataFrame:
|
25
|
+
"""Read stored feature and transform to specific statistical output.
|
26
|
+
|
27
|
+
Parameters
|
28
|
+
----------
|
29
|
+
storage : storage-like
|
30
|
+
The storage class, for example, SQLiteFeatureStorage.
|
31
|
+
feature_name : str
|
32
|
+
Name of the feature to read.
|
33
|
+
transform : str
|
34
|
+
The kind of transform formatted as ``<package>_<function>``,
|
35
|
+
for example, ``bctpy_degrees_und``.
|
36
|
+
transform_args : tuple, optional
|
37
|
+
The positional arguments for the callable of ``transform``
|
38
|
+
(default None).
|
39
|
+
transform_kw_args : dict, optional
|
40
|
+
The keyword arguments for the callable of ``transform``
|
41
|
+
(default None).
|
42
|
+
|
43
|
+
Returns
|
44
|
+
-------
|
45
|
+
pandas.DataFrame
|
46
|
+
The transformed feature as a dataframe.
|
47
|
+
|
48
|
+
Notes
|
49
|
+
-----
|
50
|
+
This function has been only tested for:
|
51
|
+
|
52
|
+
* ``bct.degrees_und``
|
53
|
+
* ``bct.strengths_und``
|
54
|
+
* ``bct.clustering_coef_wu``
|
55
|
+
* ``bct.eigenvector_centrality_und``
|
56
|
+
|
57
|
+
Using other functions may fail and require tweaking.
|
58
|
+
|
59
|
+
"""
|
60
|
+
# Set default values for args and kwargs
|
61
|
+
transform_args = transform_args or ()
|
62
|
+
transform_kw_args = transform_kw_args or {}
|
63
|
+
|
64
|
+
# Read storage
|
65
|
+
stored_data = storage.read(feature_name=feature_name) # type: ignore
|
66
|
+
# Retrieve package and function
|
67
|
+
package, func_str = transform.split("_", 1)
|
68
|
+
# Condition for package
|
69
|
+
if package == "bctpy":
|
70
|
+
# Check that "matrix" is the feature data kind
|
71
|
+
if stored_data["kind"] != "matrix":
|
72
|
+
raise_error(
|
73
|
+
msg=(
|
74
|
+
f"'{stored_data['kind']}' is not valid data kind for "
|
75
|
+
f"'{package}'"
|
76
|
+
),
|
77
|
+
klass=RuntimeError,
|
78
|
+
)
|
79
|
+
|
80
|
+
# Check bctpy import
|
81
|
+
try:
|
82
|
+
import bct
|
83
|
+
except ImportError as err:
|
84
|
+
raise_error(msg=str(err), klass=ImportError)
|
85
|
+
|
86
|
+
# Warning about function usage
|
87
|
+
if func_str not in [
|
88
|
+
"degrees_und",
|
89
|
+
"strengths_und",
|
90
|
+
"clustering_coef_wu",
|
91
|
+
"eigenvector_centrality_und",
|
92
|
+
]:
|
93
|
+
warn_with_log(
|
94
|
+
f"You are about to use '{package}.{func_str}' which has not "
|
95
|
+
"been tested to run. In case it fails, you will need to tweak"
|
96
|
+
" the code yourself."
|
97
|
+
)
|
98
|
+
|
99
|
+
# Retrieve callable object
|
100
|
+
try:
|
101
|
+
func = getattr(bct, func_str)
|
102
|
+
except AttributeError as err:
|
103
|
+
raise_error(msg=str(err), klass=AttributeError)
|
104
|
+
|
105
|
+
# Apply function and store subject-wise
|
106
|
+
output_list = []
|
107
|
+
logger.debug(
|
108
|
+
f"Computing '{package}.{func_str}' for feature {feature_name} ..."
|
109
|
+
)
|
110
|
+
for subject in range(stored_data["data"].shape[2]):
|
111
|
+
output = func(
|
112
|
+
stored_data["data"][:, :, subject],
|
113
|
+
*transform_args,
|
114
|
+
**transform_kw_args,
|
115
|
+
)
|
116
|
+
output_list.append(output)
|
117
|
+
|
118
|
+
# Create dataframe for index
|
119
|
+
idx_df = pd.DataFrame(data=stored_data["element"])
|
120
|
+
# Create multiindex from dataframe
|
121
|
+
logger.debug(
|
122
|
+
f"Generating pandas.MultiIndex for feature {feature_name} ..."
|
123
|
+
)
|
124
|
+
data_idx = pd.MultiIndex.from_frame(df=idx_df)
|
125
|
+
|
126
|
+
# Create dataframe
|
127
|
+
df = pd.DataFrame(
|
128
|
+
data=output_list,
|
129
|
+
index=data_idx,
|
130
|
+
columns=stored_data["row_headers"],
|
131
|
+
)
|
132
|
+
return df
|
133
|
+
else:
|
134
|
+
raise_error(f"Unknown package: {package}")
|
@@ -0,0 +1,179 @@
|
|
1
|
+
"""Provide tests for read-and-transform."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
import numpy as np
|
11
|
+
import pytest
|
12
|
+
|
13
|
+
from junifer.onthefly import read_transform
|
14
|
+
from junifer.storage.hdf5 import HDF5FeatureStorage
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.fixture
|
18
|
+
def vector_storage(tmp_path: Path) -> HDF5FeatureStorage:
|
19
|
+
"""Return a HDF5FeatureStorage with vector data.
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
tmp_path : pathlib.Path
|
24
|
+
The path to the test directory.
|
25
|
+
|
26
|
+
"""
|
27
|
+
storage = HDF5FeatureStorage(tmp_path / "vector_store.hdf5")
|
28
|
+
storage.store(
|
29
|
+
kind="vector",
|
30
|
+
meta={
|
31
|
+
"element": {"subject": "test"},
|
32
|
+
"dependencies": [],
|
33
|
+
"marker": {"name": "vector"},
|
34
|
+
"type": "BOLD",
|
35
|
+
},
|
36
|
+
data=np.arange(2).reshape(1, 2),
|
37
|
+
col_names=["f1", "f2"],
|
38
|
+
)
|
39
|
+
return storage
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.fixture
|
43
|
+
def matrix_storage(tmp_path: Path) -> HDF5FeatureStorage:
|
44
|
+
"""Return a HDF5FeatureStorage with matrix data.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
tmp_path : pathlib.Path
|
49
|
+
The path to the test directory.
|
50
|
+
|
51
|
+
"""
|
52
|
+
storage = HDF5FeatureStorage(tmp_path / "matrix_store.hdf5")
|
53
|
+
storage.store(
|
54
|
+
kind="matrix",
|
55
|
+
meta={
|
56
|
+
"element": {"subject": "test"},
|
57
|
+
"dependencies": [],
|
58
|
+
"marker": {"name": "matrix"},
|
59
|
+
"type": "BOLD",
|
60
|
+
},
|
61
|
+
data=np.arange(4).reshape(2, 2),
|
62
|
+
col_names=["f1", "f2"],
|
63
|
+
row_names=["g1", "g2"],
|
64
|
+
)
|
65
|
+
return storage
|
66
|
+
|
67
|
+
|
68
|
+
def test_incorrect_package(matrix_storage: HDF5FeatureStorage) -> None:
|
69
|
+
"""Test error check for incorrect package name.
|
70
|
+
|
71
|
+
Parameters
|
72
|
+
----------
|
73
|
+
matrix_storage : HDF5FeatureStorage
|
74
|
+
The HDF5FeatureStorage with matrix data, as fixture.
|
75
|
+
|
76
|
+
"""
|
77
|
+
with pytest.raises(ValueError, match="Unknown package"):
|
78
|
+
read_transform(
|
79
|
+
storage=matrix_storage, # type: ignore
|
80
|
+
feature_name="BOLD_matrix",
|
81
|
+
transform="godspeed_makemake",
|
82
|
+
)
|
83
|
+
|
84
|
+
|
85
|
+
def test_incorrect_data_kind(vector_storage: HDF5FeatureStorage) -> None:
|
86
|
+
"""Test error check for incorrect data kind.
|
87
|
+
|
88
|
+
Parameters
|
89
|
+
----------
|
90
|
+
vector_storage : HDF5FeatureStorage
|
91
|
+
The HDF5FeatureStorage with vector data, as fixture.
|
92
|
+
|
93
|
+
"""
|
94
|
+
with pytest.raises(RuntimeError, match="not valid data kind"):
|
95
|
+
read_transform(
|
96
|
+
storage=vector_storage, # type: ignore
|
97
|
+
feature_name="BOLD_vector",
|
98
|
+
transform="bctpy_gonggong",
|
99
|
+
)
|
100
|
+
|
101
|
+
|
102
|
+
def test_untested_bctpy_function(matrix_storage: HDF5FeatureStorage) -> None:
|
103
|
+
"""Test warning check for untested function of bctpy.
|
104
|
+
|
105
|
+
Parameters
|
106
|
+
----------
|
107
|
+
matrix_storage : HDF5FeatureStorage
|
108
|
+
The HDF5FeatureStorage with matrix data, as fixture.
|
109
|
+
|
110
|
+
"""
|
111
|
+
# Skip test if import fails
|
112
|
+
pytest.importorskip("bct")
|
113
|
+
|
114
|
+
with pytest.raises(ValueError):
|
115
|
+
with pytest.warns(RuntimeWarning, match="You are about to use"):
|
116
|
+
read_transform(
|
117
|
+
storage=matrix_storage, # type: ignore
|
118
|
+
feature_name="BOLD_matrix",
|
119
|
+
transform="bctpy_distance_bin",
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
def test_incorrect_bctpy_function(matrix_storage: HDF5FeatureStorage) -> None:
|
124
|
+
"""Test error check for incorrect function of bctpy.
|
125
|
+
|
126
|
+
Parameters
|
127
|
+
----------
|
128
|
+
matrix_storage : HDF5FeatureStorage
|
129
|
+
The HDF5FeatureStorage with matrix data, as fixture.
|
130
|
+
|
131
|
+
"""
|
132
|
+
# Skip test if import fails
|
133
|
+
pytest.importorskip("bct")
|
134
|
+
|
135
|
+
with pytest.raises(AttributeError, match="has no attribute"):
|
136
|
+
read_transform(
|
137
|
+
storage=matrix_storage, # type: ignore
|
138
|
+
feature_name="BOLD_matrix",
|
139
|
+
transform="bctpy_haumea",
|
140
|
+
)
|
141
|
+
|
142
|
+
|
143
|
+
@pytest.mark.parametrize(
|
144
|
+
"func",
|
145
|
+
(
|
146
|
+
"degrees_und",
|
147
|
+
"strengths_und",
|
148
|
+
"clustering_coef_wu",
|
149
|
+
"eigenvector_centrality_und",
|
150
|
+
),
|
151
|
+
)
|
152
|
+
def test_bctpy_function(
|
153
|
+
matrix_storage: HDF5FeatureStorage,
|
154
|
+
caplog: pytest.LogCaptureFixture,
|
155
|
+
func: str,
|
156
|
+
) -> None:
|
157
|
+
"""Test working function of bctpy.
|
158
|
+
|
159
|
+
Parameters
|
160
|
+
----------
|
161
|
+
matrix_storage : HDF5FeatureStorage
|
162
|
+
The HDF5FeatureStorage with matrix data, as fixture.
|
163
|
+
caplog : pytest.LogCaptureFixture
|
164
|
+
The pytest.LogCaptureFixture object.
|
165
|
+
func : str
|
166
|
+
The function to test.
|
167
|
+
|
168
|
+
"""
|
169
|
+
# Skip test if import fails
|
170
|
+
pytest.importorskip("bct")
|
171
|
+
|
172
|
+
with caplog.at_level(logging.DEBUG):
|
173
|
+
read_transform(
|
174
|
+
storage=matrix_storage, # type: ignore
|
175
|
+
feature_name="BOLD_matrix",
|
176
|
+
transform=f"bctpy_{func}",
|
177
|
+
)
|
178
|
+
assert "Computing" in caplog.text
|
179
|
+
assert "Generating" in caplog.text
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: junifer
|
3
|
-
Version: 0.0.3.
|
3
|
+
Version: 0.0.3.dev148
|
4
4
|
Summary: JUelich NeuroImaging FEature extractoR
|
5
5
|
Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
|
6
6
|
Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
|
@@ -49,6 +49,8 @@ Requires-Dist: julearn (==0.2.5) ; extra == 'docs'
|
|
49
49
|
Requires-Dist: sphinx-copybutton (==0.5.1) ; extra == 'docs'
|
50
50
|
Requires-Dist: towncrier (==22.12.0) ; extra == 'docs'
|
51
51
|
Requires-Dist: sphinxcontrib-mermaid (==0.8.1) ; extra == 'docs'
|
52
|
+
Provides-Extra: onthefly
|
53
|
+
Requires-Dist: bctpy (==0.6.0) ; extra == 'onthefly'
|
52
54
|
|
53
55
|

|
54
56
|
|
@@ -82,7 +84,9 @@ The documentation is available at [https://juaml.github.io/junifer](https://juam
|
|
82
84
|
* `data`: Module that handles data required for the library to work (e.g. parcels, coordinates).
|
83
85
|
* `datagrabber`: DataGrabber module.
|
84
86
|
* `datareader`: DataReader module.
|
87
|
+
* `external`: Module for external libraries and tools.
|
85
88
|
* `markers`: Markers module.
|
89
|
+
* `onthefly`: Transformation components (on-the-fly) module.
|
86
90
|
* `pipeline`: Pipeline module.
|
87
91
|
* `preprocess`: Preprocessing module.
|
88
92
|
* `storage`: Storage module.
|
@@ -1,5 +1,5 @@
|
|
1
|
-
junifer/__init__.py,sha256=
|
2
|
-
junifer/_version.py,sha256=
|
1
|
+
junifer/__init__.py,sha256=x1UR2jUcrUdm2HNl-3Qvyi4UUrU6ms5qm2qcmNY7zZk,391
|
2
|
+
junifer/_version.py,sha256=kvDO67P7RJ0LLRcQBePW7yJNV-eCAWLFpxAl-oBTn60,177
|
3
3
|
junifer/stats.py,sha256=KUX4jJcLWnlE34coet8EkdFypFd-td4Vtpx5LvlomVs,5879
|
4
4
|
junifer/api/__init__.py,sha256=YILu9M7SC0Ri4CVd90fELH2OnK_gvCYAXCoqBNCFE8E,257
|
5
5
|
junifer/api/cli.py,sha256=epPGiMU9szF0Dbv-_ty3hxxWPUd8KM-gqQI10TJEl2Y,9843
|
@@ -35,7 +35,7 @@ junifer/configs/juseless/datagrabbers/tests/test_ukb_vbm.py,sha256=b9hjc1mgO--PS
|
|
35
35
|
junifer/data/__init__.py,sha256=UKoiW2DfuNgWlpdqSVPWAED1tSMl9ygUHMsCnFXPp2A,507
|
36
36
|
junifer/data/coordinates.py,sha256=mPiaOdw3HY5c_5geGiQbHjKCTvj2YVTXie0Y6HLFk_I,4896
|
37
37
|
junifer/data/masks.py,sha256=oRvKzbm4Z4q3Dz1PdnsuS4t08x70s1fpbEgPeFKuy2Y,11277
|
38
|
-
junifer/data/parcellations.py,sha256=
|
38
|
+
junifer/data/parcellations.py,sha256=8br_1uc1iV_fOXjiX1q6Hh39nS2wdApzU4P09MZZBA4,48676
|
39
39
|
junifer/data/utils.py,sha256=K9quLIoWRmm2QFM8Rdy_5bYsWb_XhL0l5Uq_1Sie0kA,1274
|
40
40
|
junifer/data/VOIs/meta/CogAC_VOIs.txt,sha256=Sr5_E712OLdeQRyUcDNM0wLBvZIyO6gc9Q7KkyJHX1A,398
|
41
41
|
junifer/data/VOIs/meta/CogAR_VOIs.txt,sha256=t3NLwEVUZTPP34p15SaB3UInLrQyK-7Qc4iLBuQlZu8,189
|
@@ -60,7 +60,7 @@ junifer/data/masks/vickery-patil/GMprob0.2_cortex_3mm_NA_rm.nii.gz,sha256=jfMe_4
|
|
60
60
|
junifer/data/tests/test_coordinates.py,sha256=RCYOuaAjs2wqPeFHX-l9Bryt5Zw5mOIV0WpsH891RwA,3240
|
61
61
|
junifer/data/tests/test_data_utils.py,sha256=Vy7x8zaHws5hmn92PKSv3H38hU2kamOpyaH6nG_NNpw,1086
|
62
62
|
junifer/data/tests/test_masks.py,sha256=sJ_rXAPddQ_2SAGvew1FEEMcLwy0NEfQv8TsCvgA2pA,13266
|
63
|
-
junifer/data/tests/test_parcellations.py,sha256=
|
63
|
+
junifer/data/tests/test_parcellations.py,sha256=zAhIl2f1w6BEocSGujwZ-zveSFg8EBeQniRm_hoTxHs,32445
|
64
64
|
junifer/datagrabber/__init__.py,sha256=xKMQMjqoWul13YluGTLLMBgKahUg5jJKi4phPih3XJU,634
|
65
65
|
junifer/datagrabber/base.py,sha256=6JoIDBCWXCIQdjSg6OIbdrqbQKzV6_zBTk0Zs1Wj6_Y,4622
|
66
66
|
junifer/datagrabber/datalad_base.py,sha256=dDaBiIePPP6-G4ycgBMxTcXxs4vkg-yDS3OBURK4VGs,10731
|
@@ -146,6 +146,9 @@ junifer/markers/tests/test_marker_utils.py,sha256=SR3ADWI3uGv4ozYqVu-rMZnJVqP6Jn
|
|
146
146
|
junifer/markers/tests/test_markers_base.py,sha256=cbuCeMmjyFMP1ea6J6XRsBQo8CivQ41ooABiT1Snj2k,3076
|
147
147
|
junifer/markers/tests/test_parcel_aggregation.py,sha256=RW2EKRokmavMZB7e8WghYc6fLm6UOHzOm6xKdvBsoo0,21501
|
148
148
|
junifer/markers/tests/test_sphere_aggregation.py,sha256=1uOfnLQJ5hVCz7jcX9QG3M6htLSJSEOflKV6fXNtSZI,7652
|
149
|
+
junifer/onthefly/__init__.py,sha256=GG_Z5NgnVNph6CLPtGFI2HE_OIuVTZ874BOq72mA-ps,160
|
150
|
+
junifer/onthefly/read_transform.py,sha256=2yLlFscCZDS3z80ffD36_vF1GeKxSFfK0xNB-xsqOTM,4040
|
151
|
+
junifer/onthefly/tests/test_read_transform.py,sha256=D2C3IpXQHdsJSF07v8rEwGntLGXjZOserlRhebJUAVM,4719
|
149
152
|
junifer/pipeline/__init__.py,sha256=gOOrLf_C3oe9RdmLLjKMhs2gqtrr5Tzfi_cyTFEp8HM,238
|
150
153
|
junifer/pipeline/pipeline_step_mixin.py,sha256=fZtJa5NJ2_9r6dD_UOkXA5fOPtRjuPUbh4lnF-ILB4c,5019
|
151
154
|
junifer/pipeline/registry.py,sha256=PQq8D6DiHhSeh0tdHs9J6jv9L1WsfEUqJxzhlzAQDbE,3956
|
@@ -187,10 +190,10 @@ junifer/utils/fs.py,sha256=Jd9AoV2fIF7pT7KhXsn8T1O1fJ1_SFZgaFuOBAM7DG8,460
|
|
187
190
|
junifer/utils/logging.py,sha256=phBwOFaK6ejqbSjkCSAkZhhdo4sr01GdVZmJIL8t-Lw,8994
|
188
191
|
junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
|
189
192
|
junifer/utils/tests/test_logging.py,sha256=l8oo-AiBV7H6_IzlsNcj__cLeZBUvgIGoaMszD9VaJg,7754
|
190
|
-
junifer-0.0.3.
|
191
|
-
junifer-0.0.3.
|
192
|
-
junifer-0.0.3.
|
193
|
-
junifer-0.0.3.
|
194
|
-
junifer-0.0.3.
|
195
|
-
junifer-0.0.3.
|
196
|
-
junifer-0.0.3.
|
193
|
+
junifer-0.0.3.dev148.dist-info/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
|
194
|
+
junifer-0.0.3.dev148.dist-info/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
|
195
|
+
junifer-0.0.3.dev148.dist-info/METADATA,sha256=1P5_ZElKDMxF97PWTlPrNpGvCCv26szrvETPaIgy5UI,6706
|
196
|
+
junifer-0.0.3.dev148.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
197
|
+
junifer-0.0.3.dev148.dist-info/entry_points.txt,sha256=DxFvKq0pOqRunAK0FxwJcoDfV1-dZvsFDpD5HRqSDhw,48
|
198
|
+
junifer-0.0.3.dev148.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
|
199
|
+
junifer-0.0.3.dev148.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|