Skip to content

Commit 1377db6

Browse files
authored
[FIX] PET-Volume : computes statistics with the 4 atlases again (not only 1) (aramis-lab#1578)
* Removing the break from the code * Add unit tests * Fix unit tests
1 parent e4fa68f commit 1377db6

File tree

3 files changed

+148
-138
lines changed

3 files changed

+148
-138
lines changed

clinica/pipelines/pet/volume/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ def compute_atlas_statistics(image: Path, atlas_names: list[str]) -> list[Path]:
253253
)
254254
statistics_on_atlas(image, atlas, out_atlas_statistics)
255255
atlas_statistics_list.append(out_atlas_statistics)
256-
break # Why is there a break here ????
257256
return atlas_statistics_list
258257

259258

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from pathlib import Path
2+
3+
import pandas as pd
4+
import pytest
5+
6+
from clinica.utils.pet import SUVRReferenceRegion
7+
8+
9+
@pytest.fixture
10+
def psf_df() -> pd.DataFrame:
11+
return pd.DataFrame(
12+
{
13+
"participant_id": ["sub-CLNC01"] * 3 + ["sub-CLNC02", "sub-CLNC03"],
14+
"session_id": ["ses-M000", "ses-M018"] + ["ses-M000"] * 3,
15+
"acq_label": ["18FFDG", "18FFDG", "18FAV45", "18FFDG", "18FFDG"],
16+
"psf_x": [8, 8, 7, 8, 8],
17+
"psf_y": [9, 9, 6, 9, 9],
18+
"psf_z": [10, 10, 5, 10, 10],
19+
}
20+
)
21+
22+
23+
def test_read_psf_information_errors(tmp_path: Path, psf_df: pd.DataFrame):
24+
from clinica.pipelines.pet.utils import read_psf_information
25+
from clinica.utils.pet import Tracer
26+
27+
with pytest.raises(
28+
FileNotFoundError,
29+
match="No such file or directory: 'foo.tsv'",
30+
):
31+
read_psf_information(
32+
Path("foo.tsv"),
33+
["sub-CLNC01", "sub-CLNC01"],
34+
["ses-M000", "ses-M018"],
35+
Tracer.FDG,
36+
)
37+
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
38+
with pytest.raises(
39+
RuntimeError,
40+
match=(
41+
"Subject sub-CLNC06 with session ses-M018 and tracer 18FFDG "
42+
"that you want to proceed was not found in the TSV file containing "
43+
"PSF specifications"
44+
),
45+
):
46+
read_psf_information(
47+
tmp_path / "psf.tsv",
48+
["sub-CLNC01", "sub-CLNC06"],
49+
["ses-M000", "ses-M018"],
50+
Tracer.FDG,
51+
)
52+
psf_df_2 = pd.DataFrame(
53+
{
54+
"participant_id": ["sub-CLNC01"],
55+
"session_id": ["ses-M000"],
56+
"acq_label": ["18FFDG"],
57+
"psf_x": [10],
58+
"psf_y": [11],
59+
"psf_z": [12],
60+
}
61+
)
62+
duplicate_psf_df = pd.concat([psf_df, psf_df_2])
63+
duplicate_psf_df.to_csv(tmp_path / "duplicate_psf.tsv", sep="\t", index=False)
64+
with pytest.raises(
65+
RuntimeError,
66+
match=(
67+
"Subject sub-CLNC01 with session ses-M000 and tracer 18FFDG "
68+
"that you want to proceed was found multiple times "
69+
"in the TSV file containing PSF specifications"
70+
),
71+
):
72+
read_psf_information(
73+
tmp_path / "duplicate_psf.tsv",
74+
["sub-CLNC01", "sub-CLNC01"],
75+
["ses-M000", "ses-M018"],
76+
Tracer.FDG,
77+
)
78+
psf_df["foo"] = ["bar"] * 5
79+
psf_df.to_csv(tmp_path / "wrong_psf.tsv", sep="\t", index=False)
80+
with pytest.raises(
81+
IOError,
82+
match="must contain",
83+
):
84+
read_psf_information(
85+
tmp_path / "wrong_psf.tsv",
86+
["sub-CLNC01", "sub-CLNC01"],
87+
["ses-M000", "ses-M018"],
88+
Tracer.FDG,
89+
)
90+
psf_df.drop(["foo", "session_id"], axis=1).to_csv(
91+
tmp_path / "wrong_psf_2.tsv", sep="\t", index=False
92+
)
93+
with pytest.raises(
94+
IOError,
95+
match="must contain",
96+
):
97+
read_psf_information(
98+
tmp_path / "wrong_psf_2.tsv",
99+
["sub-CLNC01", "sub-CLNC01"],
100+
["ses-M000", "ses-M018"],
101+
Tracer.FDG,
102+
)
103+
104+
105+
def test_read_psf_information(tmp_path: Path, psf_df: pd.DataFrame):
106+
from clinica.pipelines.pet.utils import read_psf_information
107+
from clinica.utils.pet import Tracer
108+
109+
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
110+
assert read_psf_information(
111+
tmp_path / "psf.tsv",
112+
["sub-CLNC01", "sub-CLNC01"],
113+
["ses-M000", "ses-M018"],
114+
Tracer.FDG,
115+
) == [[8, 9, 10], [8, 9, 10]]
116+
# Shuffle rows in dataframe and make sure results do not depend on row order
117+
psf_df = psf_df.sample(frac=1).reset_index(drop=True)
118+
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
119+
assert read_psf_information(
120+
tmp_path / "psf.tsv",
121+
["sub-CLNC01", "sub-CLNC01"],
122+
["ses-M000", "ses-M018"],
123+
Tracer.FDG,
124+
) == [[8, 9, 10], [8, 9, 10]]
125+
126+
127+
@pytest.mark.parametrize("region", SUVRReferenceRegion)
128+
def test_get_suvr_mask(region):
129+
from clinica.pipelines.pet.utils import get_suvr_mask
130+
131+
assert get_suvr_mask(region).exists()
132+
133+
134+
@pytest.mark.parametrize("label", ["foo", "bar", "pons3", "cerebelumPons2"])
135+
def test_get_suvr_mask_error(label: str):
136+
from clinica.pipelines.pet.utils import get_suvr_mask
137+
138+
with pytest.raises(
139+
ValueError,
140+
match=f"'{label}' is not a valid SUVRReferenceRegion",
141+
):
142+
get_suvr_mask(label)
Lines changed: 6 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,11 @@
1-
from pathlib import Path
2-
3-
import pandas as pd
41
import pytest
52

6-
from clinica.utils.pet import SUVRReferenceRegion
7-
8-
9-
@pytest.fixture
10-
def psf_df() -> pd.DataFrame:
11-
return pd.DataFrame(
12-
{
13-
"participant_id": ["sub-CLNC01"] * 3 + ["sub-CLNC02", "sub-CLNC03"],
14-
"session_id": ["ses-M000", "ses-M018"] + ["ses-M000"] * 3,
15-
"acq_label": ["18FFDG", "18FFDG", "18FAV45", "18FFDG", "18FFDG"],
16-
"psf_x": [8, 8, 7, 8, 8],
17-
"psf_y": [9, 9, 6, 9, 9],
18-
"psf_z": [10, 10, 5, 10, 10],
19-
}
20-
)
21-
22-
23-
def test_read_psf_information_errors(tmp_path: Path, psf_df: pd.DataFrame):
24-
from clinica.pipelines.pet.utils import read_psf_information
25-
from clinica.utils.pet import Tracer
26-
27-
with pytest.raises(
28-
FileNotFoundError,
29-
match="No such file or directory: 'foo.tsv'",
30-
):
31-
read_psf_information(
32-
Path("foo.tsv"),
33-
["sub-CLNC01", "sub-CLNC01"],
34-
["ses-M000", "ses-M018"],
35-
Tracer.FDG,
36-
)
37-
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
38-
with pytest.raises(
39-
RuntimeError,
40-
match=(
41-
"Subject sub-CLNC06 with session ses-M018 and tracer 18FFDG "
42-
"that you want to proceed was not found in the TSV file containing "
43-
"PSF specifications"
44-
),
45-
):
46-
read_psf_information(
47-
tmp_path / "psf.tsv",
48-
["sub-CLNC01", "sub-CLNC06"],
49-
["ses-M000", "ses-M018"],
50-
Tracer.FDG,
51-
)
52-
psf_df_2 = pd.DataFrame(
53-
{
54-
"participant_id": ["sub-CLNC01"],
55-
"session_id": ["ses-M000"],
56-
"acq_label": ["18FFDG"],
57-
"psf_x": [10],
58-
"psf_y": [11],
59-
"psf_z": [12],
60-
}
61-
)
62-
duplicate_psf_df = pd.concat([psf_df, psf_df_2])
63-
duplicate_psf_df.to_csv(tmp_path / "duplicate_psf.tsv", sep="\t", index=False)
64-
with pytest.raises(
65-
RuntimeError,
66-
match=(
67-
"Subject sub-CLNC01 with session ses-M000 and tracer 18FFDG "
68-
"that you want to proceed was found multiple times "
69-
"in the TSV file containing PSF specifications"
70-
),
71-
):
72-
read_psf_information(
73-
tmp_path / "duplicate_psf.tsv",
74-
["sub-CLNC01", "sub-CLNC01"],
75-
["ses-M000", "ses-M018"],
76-
Tracer.FDG,
77-
)
78-
psf_df["foo"] = ["bar"] * 5
79-
psf_df.to_csv(tmp_path / "wrong_psf.tsv", sep="\t", index=False)
80-
with pytest.raises(
81-
IOError,
82-
match="must contain",
83-
):
84-
read_psf_information(
85-
tmp_path / "wrong_psf.tsv",
86-
["sub-CLNC01", "sub-CLNC01"],
87-
["ses-M000", "ses-M018"],
88-
Tracer.FDG,
89-
)
90-
psf_df.drop(["foo", "session_id"], axis=1).to_csv(
91-
tmp_path / "wrong_psf_2.tsv", sep="\t", index=False
92-
)
93-
with pytest.raises(
94-
IOError,
95-
match="must contain",
96-
):
97-
read_psf_information(
98-
tmp_path / "wrong_psf_2.tsv",
99-
["sub-CLNC01", "sub-CLNC01"],
100-
["ses-M000", "ses-M018"],
101-
Tracer.FDG,
102-
)
103-
104-
105-
def test_read_psf_information(tmp_path: Path, psf_df: pd.DataFrame):
106-
from clinica.pipelines.pet.utils import read_psf_information
107-
from clinica.utils.pet import Tracer
108-
109-
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
110-
assert read_psf_information(
111-
tmp_path / "psf.tsv",
112-
["sub-CLNC01", "sub-CLNC01"],
113-
["ses-M000", "ses-M018"],
114-
Tracer.FDG,
115-
) == [[8, 9, 10], [8, 9, 10]]
116-
# Shuffle rows in dataframe and make sure results do not depend on row order
117-
psf_df = psf_df.sample(frac=1).reset_index(drop=True)
118-
psf_df.to_csv(tmp_path / "psf.tsv", sep="\t", index=False)
119-
assert read_psf_information(
120-
tmp_path / "psf.tsv",
121-
["sub-CLNC01", "sub-CLNC01"],
122-
["ses-M000", "ses-M018"],
123-
Tracer.FDG,
124-
) == [[8, 9, 10], [8, 9, 10]]
125-
126-
127-
@pytest.mark.parametrize("region", SUVRReferenceRegion)
128-
def test_get_suvr_mask(region):
129-
from clinica.pipelines.pet.utils import get_suvr_mask
130-
131-
assert get_suvr_mask(region).exists()
1323

4+
def test_compute_atlas_statistics(tmp_path, mocker):
5+
from clinica.pipelines.pet.volume.utils import compute_atlas_statistics
1336

134-
@pytest.mark.parametrize("label", ["foo", "bar", "pons3", "cerebelumPons2"])
135-
def test_get_suvr_mask_error(label: str):
136-
from clinica.pipelines.pet.utils import get_suvr_mask
7+
atlases = ["foo", "bar", "foobar"]
1378

138-
with pytest.raises(
139-
ValueError,
140-
match=f"'{label}' is not a valid SUVRReferenceRegion",
141-
):
142-
get_suvr_mask(label)
9+
mocker.patch("clinica.utils.statistics.statistics_on_atlas")
10+
result = compute_atlas_statistics(tmp_path, atlases)
11+
assert len(result) == len(atlases)

0 commit comments

Comments
 (0)