forked from neurostuff/NiMARE
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_transforms.py
268 lines (226 loc) · 10.1 KB
/
test_transforms.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
"""Test nimare.transforms."""
import re
import nibabel as nib
import numpy as np
import pytest
from nimare import transforms
def test_ImageTransformer(testdata_ibma):
"""Smoke test on transforms.ImageTransformer."""
dset = testdata_ibma
z_files = dset.images["z"].tolist()
z_transformer = transforms.ImageTransformer(target="z")
new_dset = z_transformer.transform(dset)
new_z_files = new_dset.images["z"].tolist()
assert z_files[:-1] == new_z_files[:-1]
# new z statistic map should have 3 dimensions
assert len(nib.load(new_z_files[-1]).shape) == 3
assert all([nzf is not None for nzf in new_z_files])
varcope_files = dset.images["varcope"].tolist()
varcope_p_transformer = transforms.ImageTransformer(target=["varcope", "p"])
new_dset = varcope_p_transformer.transform(dset)
new_varcope_files = new_dset.images["varcope"].tolist()
assert not all([isinstance(vf, str) for vf in varcope_files])
assert all([isinstance(vf, str) for vf in new_varcope_files])
new_p_files = new_dset.images["p"].tolist()
assert all([isinstance(pf, str) for pf in new_p_files])
def test_transform_images(testdata_ibma):
"""Smoke test on transforms.transform_images."""
dset = testdata_ibma
z_files = dset.images["z"].tolist()
new_images = transforms.transform_images(
dset.images, target="z", masker=dset.masker, metadata_df=dset.metadata
)
new_z_files = new_images["z"].tolist()
assert z_files[:-1] == new_z_files[:-1]
# new z statistic map should have 3 dimensions
assert len(nib.load(new_z_files[-1]).shape) == 3
assert all([nzf is not None for nzf in new_z_files])
varcope_files = dset.images["varcope"].tolist()
new_images = transforms.transform_images(
dset.images, target="varcope", masker=dset.masker, metadata_df=dset.metadata
)
new_varcope_files = new_images["varcope"].tolist()
assert not all([isinstance(vf, str) for vf in varcope_files])
assert all([isinstance(vf, str) for vf in new_varcope_files])
def test_sample_sizes_to_dof():
"""Unit tests for transforms.sample_sizes_to_dof."""
sample_sizes = [20, 20, 20]
dof = 57
assert transforms.sample_sizes_to_dof(sample_sizes) == dof
sample_sizes = [20]
dof = 19
assert transforms.sample_sizes_to_dof(sample_sizes) == dof
def test_sample_sizes_to_sample_size():
"""Unit tests for transforms.sample_sizes_to_sample_size."""
sample_sizes = [20, 20, 20]
sample_size = 60
assert transforms.sample_sizes_to_sample_size(sample_sizes) == sample_size
sample_sizes = [20]
sample_size = 20
assert transforms.sample_sizes_to_sample_size(sample_sizes) == sample_size
def test_t_to_z():
"""Smoke test for transforms.t_to_z."""
t_arr = np.random.random(100)
z_arr = transforms.t_to_z(t_arr, dof=20)
assert z_arr.shape == t_arr.shape
t_arr2 = transforms.z_to_t(z_arr, dof=20)
assert np.allclose(t_arr, t_arr2)
NO_OUTPUT_PATTERN = re.compile(
(
r"^No clusters were found for ([\w\.0-9+-]+) at a threshold of [0-9]+\.[0-9]+$|"
r"No Z or p map for ([\w-]+), skipping..."
)
)
@pytest.mark.parametrize(
"kwargs,drop_data,add_data",
[
({"merge_strategy": "fill"}, "z", "p"),
({"merge_strategy": "replace"}, None, None),
({"merge_strategy": "demolish", "remove_subpeaks": True}, None, None),
({"merge_strategy": "fill", "two_sided": True}, "z", "p"),
(
{
"merge_strategy": "demolish",
"two_sided": True,
"z_threshold": 1.9,
},
None,
None,
),
({"merge_strategy": "demolish", "z_threshold": 10.0}, None, None),
],
)
def test_images_to_coordinates(tmp_path, caplog, testdata_ibma, kwargs, drop_data, add_data):
"""Test conversion of statistical images to coordinates."""
# only catch warnings from the transforms logger
caplog.set_level("WARNING", logger=transforms.LGR.name)
img2coord = transforms.ImagesToCoordinates(**kwargs)
tst_dset = testdata_ibma.copy()
if add_data:
tst_dset.images = transforms.transform_images(
tst_dset.images,
add_data,
tst_dset.masker,
tst_dset.metadata,
tmp_path,
)
if drop_data:
tst_dset.images = tst_dset.images.drop(columns=drop_data)
new_dset = img2coord.transform(tst_dset)
# metadata column "coordinate_source" should exist
assert "coordinate_source" in new_dset.metadata.columns
# get the studies that did not generate coordinates
# either because the threshold was too high or
# because there were no images to generate coordinates
studies_without_coordinates = []
for msg in caplog.messages:
match = NO_OUTPUT_PATTERN.match(msg)
if match:
studies_without_coordinates.append(
match.group(1) if match.group(1) else match.group(2)
)
# if there is not a z map for a study contrast, raise a warning
# unless the strategy is fill since all studies already have coordinates
if drop_data == "z" and add_data == "p" and img2coord.merge_strategy != "fill":
assert "No Z map for" in caplog.messages[0]
# if someone is trying to use two-sided on a study contrast with a p map, raise a warning
if img2coord.two_sided:
assert "Cannot use two_sided threshold using a p map for" in caplog.messages[0]
# if two_sided was specified and z maps were used, there
# should be peaks with negative values.
if img2coord.two_sided and not drop_data and not add_data:
assert np.any(new_dset.coordinates["z_stat"] < 0.0)
# since testdata_ibma already has coordinate data for every study
# this transformation should retain the same number of unique ids
# unless the merge_strategy was demolish
if img2coord.merge_strategy == "demolish":
expected_studies_with_coordinates = set(
tst_dset.images.loc[~tst_dset.images["z"].isnull(), "id"]
) - set(studies_without_coordinates)
else:
expected_studies_with_coordinates = set(tst_dset.coordinates["id"]).union(
["pain_01.nidm-1"]
)
assert set(new_dset.coordinates["id"]) == expected_studies_with_coordinates, set(
new_dset.coordinates["id"]
)
def test_ddimages_to_coordinates_merge_strategy(testdata_ibma):
"""Test different merging strategies."""
img2coord = transforms.ImagesToCoordinates(z_threshold=1.9)
# keep pain_01.nidm-1, pain_02.nidm-1, pain_03.nidm-1, pain_04.nidm-1
tst_dset = testdata_ibma.slice(
["pain_01.nidm-1", "pain_02.nidm-1", "pain_03.nidm-1", "pain_04.nidm-1"]
)
# remove image data for pain_01.nidm-1 and pain_03.nidm-1
# coordinate data for pain_01.nidm-1 and pain_02.nidm-1 are already removed
tst_dset.images = tst_dset.images.query("id != 'pain_01.nidm-1'")
tst_dset.images = tst_dset.images.query("id != 'pain_03.nidm-1'")
# | study | image | coordinate |
# |--------------|-------|------------|
# | pain_01.nidm | no | no |
# | pain_02.nidm | yes | no |
# | pain_03.nidm | no | yes |
# | pain_04.nidm | yes | yes |
# test 'fill' strategy
# only pain_02.nidm should have new data.
# pain_01.nidm, pain_03.nidm, and pain_04.nidm should remain the same
img2coord.merge_strategy = "fill"
fill_dset = img2coord.transform(tst_dset)
# pain_01.nidm and pain_03.nidm should be unchanged
assert set(fill_dset.coordinates.query("id != 'pain_02.nidm-1'")["x"]) == set(
tst_dset.coordinates["x"]
)
# pain_02.nidm should be in the coordinates now
assert "pain_02.nidm-1" in fill_dset.coordinates["id"].unique()
# test 'replace' strategy
# pain_02.nidm and pain_04.nidm should have new data,
# but pain_01.nidm and pain_03.nidm should remain the same
img2coord.merge_strategy = "replace"
replace_dset = img2coord.transform(tst_dset)
# pain_01.nidm should remain the same
assert set(replace_dset.coordinates.query("id == 'pain_01.nidm-1'")["x"]) == set(
tst_dset.coordinates.query("id == 'pain_01.nidm-1'")["x"]
)
# pain_02.nidm should be new
assert "pain_02.nidm-1" in replace_dset.coordinates["id"].unique()
# pain_03.nidm should remain the same
assert set(replace_dset.coordinates.query("id == 'pain_03.nidm-1'")["x"]) == set(
tst_dset.coordinates.query("id == 'pain_03.nidm-1'")["x"]
)
# pain_04.nidm should be new (and have different coordinates from the old version)
assert set(replace_dset.coordinates.query("id == 'pain_04.nidm-1'")["x"]) != set(
tst_dset.coordinates.query("id == 'pain_04.nidm-1'")["x"]
)
# test 'demolish' strategy
# pain_03.nidm will be removed, and pain_02.nidm and pain_04.nidm will be new
img2coord.merge_strategy = "demolish"
demolish_dset = img2coord.transform(tst_dset)
# pain_01.nidm should not be in the dset
assert "pain_01.nidm-1" not in demolish_dset.coordinates["id"].unique()
# pain_02.nidm should be new
assert "pain_02.nidm-1" in demolish_dset.coordinates["id"].unique()
# pain_03.nidm should not be in the dset
assert "pain_03.nidm-1" not in demolish_dset.coordinates["id"].unique()
# pain_04.nidm should be new (and have different coordinates from the old version)
assert set(demolish_dset.coordinates.query("id == 'pain_04.nidm-1'")["x"]) != set(
tst_dset.coordinates.query("id == 'pain_04.nidm-1'")["x"]
)
@pytest.mark.parametrize(
"expected_z,tail,expected_p",
[
(0.0, "two", 1.0),
(0.0, "one", 0.5),
(1.959963, "two", 0.05),
(1.959963, "one", 0.025),
(-1.959963, "one", 0.975),
(-1.959963, "two", 0.05),
([0.0, 1.959963, -1.959963], "two", [1.0, 0.05, 0.05]),
([0.0, 1.959963, -1.959963], "one", [0.5, 0.025, 0.975]),
],
)
def test_z_to_p(expected_z, tail, expected_p):
"""Test z to p, and p to z conversion."""
p = transforms.z_to_p(expected_z, tail)
z = transforms.p_to_z(expected_p, tail) * np.sign(expected_z)
assert np.all(np.isclose(p, expected_p))
assert np.all(np.isclose(z, expected_z))