Feat/coordinates #71

Merged
fraimondo merged 4 commits from feat/coordinates into main 2022-09-14 13:06:44 +00:00
18 changed files with 483 additions and 0 deletions

View file

@ -31,6 +31,8 @@ Enhancements
- Implement matrix storage in SQliteFeatureStorage (:gh:`42` by `Fede Raimondo`_).
- Implement coordinate register, list and load (:gh:`11` by `Fede Raimondo`_).
Bugs
~~~~

View file

@ -0,0 +1,19 @@
36.0 22.0 -4.0 RaIns
2.0 16.0 48.0 preSMA
48.0 12.0 30.0 rIFGp
36.0 2.0 54.0 rdPMC
48.0 30.0 24.0 rIFGa
-38.0 -44.0 46.0 lIPS
-24.0 -66.0 48.0 lSPL
40.0 -46.0 46.0 rIPS
60.0 -44.0 24.0 rIPC
30.0 -62.0 52.0 rSPL
-44.0 10.0 30.0 lIFG
-34.0 20.0 -4.0 LaIns
-26.0 2.0 52.0 ldPMC
6.0 -18.0 -2.0 rThal
-40.0 -66.0 -10.0 lIOG
48.0 19.0 6.0 rIFG
8.0 29.0 30.0 aMCC
-45.0 27.0 30.0 lIFG
11.0 7.0 7.0 rNcaud

View file

@ -0,0 +1,9 @@
-40 -64 -12 Fusiform_L
36 22 -4 Insula_R
-44 10 32 Precentral_L
60 -44 24 Temporal_Sup_R
0 18 48 Supp_Motor_Area_L
-36 -46 46 Parietal_Inf_L
38 -46 44 Parietal_Inf_R
-26 0 54 Frontal_Mid_L

View file

@ -0,0 +1,7 @@
0 -53 26 PCC
0 52 -6 MPFC
-48 -62 36 lAG
46 -62 32 rAG
-24 -22 -20 lHF
24 -22 -20 rHF

View file

@ -0,0 +1,22 @@
2.0 56.0 18.0 dmPFC
-8.0 54.0 34.0 dmPFC
36.0 22.0 -8.0 raI
-30.0 20.0 4.0 laI
50.0 12.0 -8.0 rIFG
54.0 16.0 20.0 rIFG/Area44
50.0 30.0 4.0 rIFG/Area45
-44.0 24.0 -6.0 lIFG
-4.0 18.0 50.0 SMA
-2.0 28.0 20.0 aMCC
-4.0 42.0 18.0 rACC
-2.0 -32.0 28.0 PCC
52.0 -58.0 22.0 rTPJ
-56.0 -58.0 22.0 lTPJ
22.0 -2.0 -16.0 rAm
54.0 -8.0 -16.0 rMTG
52.0 -36.0 2.0 rpSTS
-12.0 -4.0 12.0 laTh
6.0 -32.0 2.0 rpTh
26.0 -26.0 -12.0 rHippo
2.0 -20.0 -12.0 Midbrain
14.0 4.0 0.0 rGP

View file

@ -0,0 +1,10 @@
-39.0 -21.0 54.0 lSMC*
41.0 -16.0 57.0 rSMC*
-3.0 -2.0 54.0 SMA
-57.0 2.0 32.0 lPMCv
-53.0 -24.0 21.0 lIPC
45.0 -38.0 48.0 rIPC
-23.0 -7.0 1.0 lBG
25.0 -8.0 3.0 rBG
-22.0 -52.0 26.0 lCba
18.0 -54.0 -22.0 rCba

View file

@ -0,0 +1,10 @@
-34 22 -4 leftInsula
34 24 0 rightInsula
-26 0 52 Frontal_Mid_L
44 38 28 Frontal_Inf_Tri_R
46 10 28 Frontal_Inf_Oper_R
-6 18 50 Supp_Motor_Area_L
-34 -52 56 Parietal_Inf_L
32 -52 50 Parietal_Inf_R
32 6 58 Frontal_Mid_R

View file

@ -0,0 +1,18 @@
38.0 18.0 0.0 rIns
52.0 12.0 -4.0 rSTG
60.0 6.0 2.0 rTP
22.0 0.0 -4.0 rPall
-38.0 14.0 4.0 lIns
-58.0 0.0 6.0 lOP4
-20.0 6.0 2.0 lPut
4.0 6.0 46.0 rSMA
0.0 14.0 36.0 lMCC
-42.0 -18.0 18.0 lOP3
-54.0 -24.0 24.0 lSMG
-36.0 -20.0 2.0 lIns
-14.0 -12.0 10.0 lTh
10.0 -18.0 4.0 rTh
56.0 -24.0 24.0 lSMG
44.0 -14.0 16.0 rOP3
38.0 50.0 12.0 rMFG
-24.0 -66.0 -26.0 lCb

View file

@ -0,0 +1,25 @@
12.0 10.0 -6.0 rNAc
-10.0 8.0 -4.0 lPall
36.0 20.0 -6.0 rIns
-32.0 20.0 -4.0 lIns
0.0 24.0 40.0 dmPFC
0.0 54.0 -8.0 medOFC
24.0 -2.0 -16.0 rAm
6.0 -14.0 8.0 rTh
-6.0 -16.0 8.0 lTh
0.0 8.0 48.0 SMA
8.0 -18.0 -10.0 rBrainStem
-6.0 -18.0 -10.0 lBrainStem
2.0 44.0 20.0 ACC
-24.0 2.0 52.0 lMFG
-38.0 -4.0 6.0 lIns(Id3)
24.0 40.0 -14.0 rMidOFC(Fo3)
-16.0 42.0 -14.0 lMidOFC(Fo3)
40.0 32.0 32.0 raMFG
-28.0 -56.0 48.0 lIPL(IPS)
28.0 -58.0 50.0 rAG
0.0 -32.0 32.0 PCC
-36.0 50.0 10.0 lFP
-46.0 42.0 -4.0 lLOFC
30.0 4.0 50.0 rpMFG
-22.0 30.0 48.0 lSFG

View file

@ -0,0 +1,10 @@
-48.0 -20.0 20.0 S1_L
-54.0 -20.0 48.0 postcentralG_S1_L
-44.0 -26.0 58.0 postcentralG_S1_L
-38.0 -12.0 4.0 Ins_claustrum_L
-40.0 4.0 10.0 pars_opercularis_Ins_L
56.0 -22.0 20.0 SMG_R
56.0 -34.0 18.0 pSTG_R
56.0 -38.0 28.0 IPL_SMG_R
60.0 -20.0 32.0 postcentralG_R
-4.0 14.0 36.0 MCC_L

View file

@ -0,0 +1,15 @@
0.0 52.0 -12.0 vmPFC
2.0 58.0 12.0 FP
-8.0 56.0 30.0 dmPFC
2.0 -56.0 30.0 Prc
56.0 -50.0 18.0 rTPJ
-48.0 -56.0 24.0 lTPJ
54.0 -2.0 -20.0 rTP
-54.0 -2.0 -24.0 lTP
52.0 -18.0 -12.0 rMTG
-54.0 -28.0 -4.0 lMTG
50.0 -34.0 0.0 rpSTS
-58.0 -44.0 4.0 lpSTS
54.0 28.0 6.0 rIFG
-48.0 30.0 -12.0 lIFG
48.0 -72.0 8.0 rV5

View file

@ -0,0 +1,16 @@
-2.0 8.0 50.0 aParacentralL
8.0 32.0 46.0 rmpSFG
0.0 26.0 34.0 dMCC
50.0 8.0 32.0 rIFJ
40.0 22.0 -4.0 raI
46.0 36.0 20.0 rIFS
-40.0 -12.0 60.0 lPrecentralG
-46.0 -68.0 -6.0 lIOG
-48.0 8.0 30.0 lIFJ
62.0 -38.0 17.0 rTPJ
8.0 -12.0 6.0 rTh
32.0 -90.0 4.0 rMOG
-42.0 12.0 -2.0 laI
-10.0 -14.0 6.0 lTh
6.0 -58.0 -18.0 Cb
44.0 -44.0 46.0 rIPL

View file

@ -0,0 +1,23 @@
-32.0 22.0 -2.0 aIns_l
-48.0 10.0 26.0 IFG_l
-46.0 26.0 24.0 lPFCc_l
-38.0 50.0 10.0 lPFCr_l
36.0 22.0 -6.0 aIns_r
50.0 14.0 24.0 IFG_r
44.0 34.0 32.0 lPFCc_r
38.0 54.0 6.0 lPFCr_l
2.0 18.0 48.0 pmFC
-28.0 0.0 56.0 psFC
30.0 2.0 56.0 psFC
-42.0 -42.0 46.0 IPS_l
-34.0 -52.0 48.0 SPL_l
-24.0 -66.0 54.0 SPLp_l
42.0 -44.0 44.0 IPSa_r
32.0 -58.0 48.0 IPSp_r
16.0 -66.0 56.0 SPLp_r
-12.0 -12.0 12.0 Thal_l
-16.0 2.0 14.0 Ncaud_l
-16.0 0.0 2.0 GP_l
12.0 -10.0 10.0 Thal_r
-34.0 -66.0 -20.0 Cb_FG_l
32.0 -64.0 -18.0 Cb_FG_r

View file

@ -0,0 +1,17 @@
-46.0 6.0 30.0 IFG_l
50.0 12.0 28.0 IFG_r
-32.0 20.0 2.0 aIns_l
36.0 22.0 0.0 aIns_r
-4.0 14.0 44.0 SMA_l
6.0 18.0 46.0 SMA_r
-32.0 -52.0 46.0 IPS_l
32.0 -58.0 48.0 IPS_r
44.0 36.0 20.0 MFG_r
-28.0 -4.0 52.0 dPMC_l
-44.0 32.0 22.0 MFG_l
32.0 0.0 52.0 dPMC_r
-20.0 6.0 4.0 Put_l
10.0 -12.0 8.0 Thal_r
-46.0 -60.0 -10.0 ITG_l
22.0 6.0 4.0 Put_r
-10.0 -16.0 6.0 Thal_l

View file

@ -0,0 +1,12 @@
0.0 38.0 10.0 ACC
-24.0 -10.0 -20.0 AmyHipp_L
24.0 -8.0 -22.0 AmyHipp_R
-2.0 -52.0 26.0 PrC
-2.0 32.0 -8.0 SGC
-46.0 -66.0 18.0 TPJ_L
50.0 -60.0 18.0 TPJ_R
-2.0 52.0 14.0 dmPFC
-6.0 10.0 -8.0 vBG_L
6.0 10.0 -8.0 vBG_R
-2.0 50.0 -10.0 vmPFC
-54.0 -10.0 -20.0 aMTS/aMTG

View file

@ -0,0 +1,16 @@
0 -53 6 PCC
0 52 -6 MPFC
-48 -62 36 lAG
46 -62 32 rAG
-24 -22 -20 lHF
24 -22 -20 rHF
10 -22 42 Middlecingulate
-48 -20 38 lIPG
0 -48 -30 cerebellum
34 -80 -34 rCerebellum
56 30 8 rdlPFC
-42 -82 10 lateraloccipital
-54 24 10 rdrPFC
22 34 54 RSFG
-50 14 -40 lTempP
-38 14 54 leftmiddlefrontalgyrus(BA6)

150
junifer/data/coordinates.py Normal file
View file

@ -0,0 +1,150 @@
"""Provide functions for list of coordinates."""
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
# License: AGPL
from pathlib import Path
from typing import Dict, List, Union, Optional, Tuple
import typing
import pandas as pd
import numpy as np
from numpy.typing import ArrayLike
from ..utils.logging import logger, raise_error
# Path to the VOIs
_vois_path = Path(__file__).parent / "VOIs"
# Path to the metadata of the VOIs
_vois_meta_path = _vois_path / "meta"
"""
A dictionary containing all supported coordinates and their respective file or
data.
The built-in coordinates are files that are shipped with the package in the
data/VOIs directory. The user can also register their own coordinates, which
will be stored as numpy arrays in the dictionary.
"""
_available_coordinates: Dict[
str, Union[Path, Dict[str, Union[ArrayLike, List[str]]]]
] = {
"CogAC": _vois_meta_path / "CogAC_VOIs.txt",
"CogAR": _vois_meta_path / "CogAR_VOIs.txt",
"DMNBuckner": _vois_meta_path / "DMNBuckner_VOIs.txt",
"eMDN": _vois_meta_path / "eMDN_VOIs.txt",
"Empathy": _vois_meta_path / "Empathy_VOIs.txt",
"eSAD": _vois_meta_path / "eSAD_VOIs.txt",
"extDMN": _vois_meta_path / "extDMN_VOIs.txt",
"Motor": _vois_meta_path / "Motor_VOIs.txt",
"MultiTask": _vois_meta_path / "MultiTask_VOIs.txt",
"PhysioStress": _vois_meta_path / "PhysioStress_VOIs.txt",
"Rew": _vois_meta_path / "Rew_VOIs.txt",
"Somatosensory": _vois_meta_path / "Somatosensory_VOIs.txt",
"ToM": _vois_meta_path / "ToM_VOIs.txt",
"VigAtt": _vois_meta_path / "VigAtt_VOIs.txt",
"WM": _vois_meta_path / "WM_VOIs.txt",
}
def register_coordinates(
name: str,
coordinates: ArrayLike,
voi_names: List[str],
overwrite: Optional[bool] = False,
) -> None:
"""Register coordinates.
Parameters
----------
name : str
The name of the coordinates.
coordinates : numpy.ndarray
The coordinates.
voi_names : list of str
The names of the VOIs.
overwrite : bool, optional
If True, overwrite an existing list of coordinates with the same name.
Does not apply to built-in coordinates (default False).
"""
if name in _available_coordinates:
if isinstance(_available_coordinates[name], Path):
raise_error(
f"Coordinates {name} already registered as built-in "
"coordinates."
)
if overwrite is True:
logger.info(f"Overwriting coordinates {name}")
else:
raise_error(
f"Coordinates {name} already registered. "
"Set `overwrite=True` to update its value."
)
if not isinstance(coordinates, np.ndarray):
raise_error(
f"Coordinates must be a numpy.ndarray, not {type(coordinates)}."
)
if coordinates.ndim != 2:
raise_error(
f"Coordinates must be a 2D array, not {coordinates.ndim}D."
)
if coordinates.shape[1] != 3:
raise_error(
f"Each coordinate must have 3 values, not {coordinates.shape[1]} "
)
if len(voi_names) != coordinates.shape[0]:
raise_error(
f"Length of voi_names ({len(voi_names)}) does not match the "
f"number of coordinates ({coordinates.shape[0]})."
)
_available_coordinates[name] = {
"coords": coordinates,
"voi_names": voi_names,
}
def list_coordinates() -> List[str]:
"""List all the available coordinates lists (VOIs).
Returns
-------
list of str
A list with all available coordinates names.
"""
return sorted(_available_coordinates.keys())
def load_coordinates(name: str) -> Tuple[ArrayLike, List[str]]:
"""Load coordinates.
Parameters
----------
name : str
The name of the coordinates.
Returns
-------
numpy.ndarray
The coordinates.
list of str
The names of the VOIs.
"""
if name not in _available_coordinates:
raise_error(f"Coordinates {name} not found.")
t_coord = _available_coordinates[name]
if isinstance(t_coord, Path):
df_coords = pd.read_csv(t_coord, sep="\t", header=None)
coords = df_coords.iloc[:, [0, 1, 2]].to_numpy()
names = [x for x in df_coords.iloc[:, [3]].values[:, 0]]
else:
coords = t_coord["coords"]
coords = typing.cast(ArrayLike, coords)
names = t_coord["voi_names"]
names = typing.cast(List[str], names)
return coords, names

View file

@ -0,0 +1,102 @@
"""Provide tests for coordinates."""
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
# License: AGPL
import pytest
import numpy as np
from numpy.testing import assert_array_equal
from junifer.data.coordinates import (
list_coordinates,
load_coordinates,
register_coordinates,
)
def test_register_coordinates_built_in_check() -> None:
"""Test coordinates registration check for built-in atlas."""
with pytest.raises(ValueError, match=r"built-in"):
register_coordinates(
name="DMNBuckner",
coordinates=np.zeros(2),
voi_names=["1", "2"],
overwrite=True,
)
def test_register_coordinates_overwrite() -> None:
"""Test coordinates registration check for overwriting."""
register_coordinates(
name="MyList",
coordinates=np.zeros((2, 3)),
voi_names=["roi1", "roi2"],
overwrite=True,
)
with pytest.raises(ValueError, match=r"already registered"):
register_coordinates(
name="MyList",
coordinates=np.ones((2, 3)),
voi_names=["roi2", "roi3"],
)
register_coordinates(
name="MyList",
coordinates=np.ones((2, 3)),
voi_names=["roi2", "roi3"],
overwrite=True,
)
coord, names = load_coordinates("MyList")
assert_array_equal(coord, np.ones((2, 3)))
assert names == ["roi2", "roi3"]
def test_register_coordinates_valid_input() -> None:
"""Test coordinates registration check for valid input."""
with pytest.raises(ValueError, match=r"numpy.ndarray"):
register_coordinates(
name="MyList",
coordinates=[1, 2],
voi_names=["roi1", "roi2"],
overwrite=True,
)
with pytest.raises(ValueError, match=r"2D array"):
register_coordinates(
name="MyList",
coordinates=np.zeros((2, 3, 4)),
voi_names=["roi1", "roi2"],
overwrite=True,
)
with pytest.raises(ValueError, match=r"3 values"):
register_coordinates(
name="MyList",
coordinates=np.zeros((2, 4)),
voi_names=["roi1", "roi2"],
overwrite=True,
)
with pytest.raises(ValueError, match=r"voi_names"):
register_coordinates(
name="MyList",
coordinates=np.zeros((2, 3)),
voi_names=["roi1", "roi2", "roi3"],
overwrite=True,
)
def test_list_coordinates() -> None:
"""Test listing of available coordinates."""
available_coordinates = list_coordinates()
assert "DMNBuckner" in available_coordinates
assert "MultiTask" in available_coordinates
assert "VigAtt" in available_coordinates
assert "WM" in available_coordinates
def test_load_coordinates() -> None:
"""Test loading coordinates from file."""
coord, names = load_coordinates("DMNBuckner")
assert coord.shape == (6, 3) # type: ignore
assert names == ["PCC", "MPFC", "lAG", "rAG", "lHF", "rHF"]