nimare 0.4.2__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.
- benchmarks/__init__.py +0 -0
- benchmarks/bench_cbma.py +57 -0
- nimare/__init__.py +45 -0
- nimare/_version.py +21 -0
- nimare/annotate/__init__.py +21 -0
- nimare/annotate/cogat.py +213 -0
- nimare/annotate/gclda.py +924 -0
- nimare/annotate/lda.py +147 -0
- nimare/annotate/text.py +75 -0
- nimare/annotate/utils.py +87 -0
- nimare/base.py +217 -0
- nimare/cli.py +124 -0
- nimare/correct.py +462 -0
- nimare/dataset.py +685 -0
- nimare/decode/__init__.py +33 -0
- nimare/decode/base.py +115 -0
- nimare/decode/continuous.py +462 -0
- nimare/decode/discrete.py +753 -0
- nimare/decode/encode.py +110 -0
- nimare/decode/utils.py +44 -0
- nimare/diagnostics.py +510 -0
- nimare/estimator.py +139 -0
- nimare/extract/__init__.py +19 -0
- nimare/extract/extract.py +466 -0
- nimare/extract/utils.py +295 -0
- nimare/generate.py +331 -0
- nimare/io.py +667 -0
- nimare/meta/__init__.py +39 -0
- nimare/meta/cbma/__init__.py +6 -0
- nimare/meta/cbma/ale.py +951 -0
- nimare/meta/cbma/base.py +947 -0
- nimare/meta/cbma/mkda.py +1361 -0
- nimare/meta/cbmr.py +970 -0
- nimare/meta/ibma.py +1683 -0
- nimare/meta/kernel.py +501 -0
- nimare/meta/models.py +1199 -0
- nimare/meta/utils.py +494 -0
- nimare/nimads.py +492 -0
- nimare/reports/__init__.py +24 -0
- nimare/reports/base.py +664 -0
- nimare/reports/default.yml +123 -0
- nimare/reports/figures.py +651 -0
- nimare/reports/report.tpl +160 -0
- nimare/resources/__init__.py +1 -0
- nimare/resources/atlases/Harvard-Oxford-LICENSE +93 -0
- nimare/resources/atlases/HarvardOxford-cort-maxprob-thr25-2mm.nii.gz +0 -0
- nimare/resources/database_file_manifest.json +142 -0
- nimare/resources/english_spellings.csv +1738 -0
- nimare/resources/filenames.json +32 -0
- nimare/resources/neurosynth_laird_studies.json +58773 -0
- nimare/resources/neurosynth_stoplist.txt +396 -0
- nimare/resources/nidm_pain_dset.json +1349 -0
- nimare/resources/references.bib +541 -0
- nimare/resources/semantic_knowledge_children.txt +325 -0
- nimare/resources/semantic_relatedness_children.txt +249 -0
- nimare/resources/templates/MNI152_2x2x2_brainmask.nii.gz +0 -0
- nimare/resources/templates/tpl-MNI152NLin6Asym_res-01_T1w.nii.gz +0 -0
- nimare/resources/templates/tpl-MNI152NLin6Asym_res-01_desc-brain_mask.nii.gz +0 -0
- nimare/resources/templates/tpl-MNI152NLin6Asym_res-02_T1w.nii.gz +0 -0
- nimare/resources/templates/tpl-MNI152NLin6Asym_res-02_desc-brain_mask.nii.gz +0 -0
- nimare/results.py +225 -0
- nimare/stats.py +276 -0
- nimare/tests/__init__.py +1 -0
- nimare/tests/conftest.py +229 -0
- nimare/tests/data/amygdala_roi.nii.gz +0 -0
- nimare/tests/data/data-neurosynth_version-7_coordinates.tsv.gz +0 -0
- nimare/tests/data/data-neurosynth_version-7_metadata.tsv.gz +0 -0
- nimare/tests/data/data-neurosynth_version-7_vocab-terms_source-abstract_type-tfidf_features.npz +0 -0
- nimare/tests/data/data-neurosynth_version-7_vocab-terms_vocabulary.txt +100 -0
- nimare/tests/data/neurosynth_dset.json +2868 -0
- nimare/tests/data/neurosynth_laird_studies.json +58773 -0
- nimare/tests/data/nidm_pain_dset.json +1349 -0
- nimare/tests/data/nimads_annotation.json +1 -0
- nimare/tests/data/nimads_studyset.json +1 -0
- nimare/tests/data/test_baseline.txt +2 -0
- nimare/tests/data/test_pain_dataset.json +1278 -0
- nimare/tests/data/test_pain_dataset_multiple_contrasts.json +1242 -0
- nimare/tests/data/test_sleuth_file.txt +18 -0
- nimare/tests/data/test_sleuth_file2.txt +10 -0
- nimare/tests/data/test_sleuth_file3.txt +5 -0
- nimare/tests/data/test_sleuth_file4.txt +5 -0
- nimare/tests/data/test_sleuth_file5.txt +5 -0
- nimare/tests/test_annotate_cogat.py +32 -0
- nimare/tests/test_annotate_gclda.py +86 -0
- nimare/tests/test_annotate_lda.py +27 -0
- nimare/tests/test_dataset.py +99 -0
- nimare/tests/test_decode_continuous.py +132 -0
- nimare/tests/test_decode_discrete.py +92 -0
- nimare/tests/test_diagnostics.py +168 -0
- nimare/tests/test_estimator_performance.py +385 -0
- nimare/tests/test_extract.py +46 -0
- nimare/tests/test_generate.py +247 -0
- nimare/tests/test_io.py +294 -0
- nimare/tests/test_meta_ale.py +298 -0
- nimare/tests/test_meta_cbmr.py +295 -0
- nimare/tests/test_meta_ibma.py +240 -0
- nimare/tests/test_meta_kernel.py +209 -0
- nimare/tests/test_meta_mkda.py +234 -0
- nimare/tests/test_nimads.py +21 -0
- nimare/tests/test_reports.py +110 -0
- nimare/tests/test_stats.py +101 -0
- nimare/tests/test_transforms.py +272 -0
- nimare/tests/test_utils.py +200 -0
- nimare/tests/test_workflows.py +221 -0
- nimare/tests/utils.py +126 -0
- nimare/transforms.py +907 -0
- nimare/utils.py +1367 -0
- nimare/workflows/__init__.py +14 -0
- nimare/workflows/base.py +189 -0
- nimare/workflows/cbma.py +165 -0
- nimare/workflows/ibma.py +108 -0
- nimare/workflows/macm.py +77 -0
- nimare/workflows/misc.py +65 -0
- nimare-0.4.2.dist-info/LICENSE +21 -0
- nimare-0.4.2.dist-info/METADATA +124 -0
- nimare-0.4.2.dist-info/RECORD +119 -0
- nimare-0.4.2.dist-info/WHEEL +5 -0
- nimare-0.4.2.dist-info/entry_points.txt +2 -0
- nimare-0.4.2.dist-info/top_level.txt +2 -0
@@ -0,0 +1,200 @@
|
|
1
|
+
"""Test nimare.utils."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import os
|
5
|
+
import os.path as op
|
6
|
+
|
7
|
+
import nibabel as nib
|
8
|
+
import numpy as np
|
9
|
+
import pytest
|
10
|
+
|
11
|
+
from nimare import utils
|
12
|
+
from nimare.meta.utils import _apply_liberal_mask
|
13
|
+
|
14
|
+
|
15
|
+
def test_find_stem():
|
16
|
+
"""Test nimare.utils._find_stem."""
|
17
|
+
test_array = [
|
18
|
+
"/home/data/dataset/file1.nii.gz",
|
19
|
+
"/home/data/dataset/file2.nii.gz",
|
20
|
+
"/home/data/dataset/file3.nii.gz",
|
21
|
+
"/home/data/dataset/file4.nii.gz",
|
22
|
+
"/home/data/dataset/file5.nii.gz",
|
23
|
+
]
|
24
|
+
stem = utils._find_stem(test_array)
|
25
|
+
assert stem == "/home/data/dataset/file"
|
26
|
+
|
27
|
+
test_array = [
|
28
|
+
"/home/data/dataset/subfolder1/file1.nii.gz",
|
29
|
+
"/home/data/dataset/subfolder1/file2.nii.gz",
|
30
|
+
"/home/data/dataset/subfolder2/file3.nii.gz",
|
31
|
+
"/home/data/dataset/subfolder2/file4.nii.gz",
|
32
|
+
"/home/data/dataset/subfolder3/file5.nii.gz",
|
33
|
+
]
|
34
|
+
stem = utils._find_stem(test_array)
|
35
|
+
assert stem == "/home/data/dataset/subfolder"
|
36
|
+
|
37
|
+
test_array = [
|
38
|
+
"/home/data/file1_test-filename_test.nii.gz",
|
39
|
+
"/home/data/file2_test-filename_test.nii.gz",
|
40
|
+
"/home/data/file3_test-filename_test.nii.gz",
|
41
|
+
"/home/data/file4_test-filename_test.nii.gz",
|
42
|
+
"/home/data/file5_test-filename_test.nii.gz",
|
43
|
+
]
|
44
|
+
stem = utils._find_stem(test_array)
|
45
|
+
assert stem == "/home/data/file"
|
46
|
+
|
47
|
+
test_array = [
|
48
|
+
"souse",
|
49
|
+
"youse",
|
50
|
+
"house",
|
51
|
+
"mouse",
|
52
|
+
"louse",
|
53
|
+
]
|
54
|
+
stem = utils._find_stem(test_array)
|
55
|
+
assert stem == ""
|
56
|
+
|
57
|
+
|
58
|
+
def test_get_template():
|
59
|
+
"""Test nimare.utils.get_template."""
|
60
|
+
# 1mm template
|
61
|
+
img = utils.get_template(space="mni152_1mm", mask=None)
|
62
|
+
assert isinstance(img, nib.Nifti1Image)
|
63
|
+
assert not nib.is_proxy(img.dataobj)
|
64
|
+
img = utils.get_template(space="mni152_1mm", mask="brain")
|
65
|
+
assert isinstance(img, nib.Nifti1Image)
|
66
|
+
|
67
|
+
# 2mm template (default)
|
68
|
+
img = utils.get_template(space="mni152_2mm", mask=None)
|
69
|
+
assert isinstance(img, nib.Nifti1Image)
|
70
|
+
img = utils.get_template(space="mni152_2mm", mask="brain")
|
71
|
+
assert isinstance(img, nib.Nifti1Image)
|
72
|
+
assert not nib.is_proxy(img.dataobj)
|
73
|
+
|
74
|
+
# ALE template
|
75
|
+
img = utils.get_template(space="ale_2mm", mask=None)
|
76
|
+
assert isinstance(img, nib.Nifti1Image)
|
77
|
+
img = utils.get_template(space="ale_2mm", mask="brain")
|
78
|
+
assert isinstance(img, nib.Nifti1Image)
|
79
|
+
assert not nib.is_proxy(img.dataobj)
|
80
|
+
|
81
|
+
# Expect exceptions when incompatible spaces or masks are requested.
|
82
|
+
with pytest.raises(ValueError):
|
83
|
+
utils.get_template(space="something", mask=None)
|
84
|
+
|
85
|
+
with pytest.raises(ValueError):
|
86
|
+
utils.get_template(space="mni152_1mm", mask="gm")
|
87
|
+
|
88
|
+
with pytest.raises(ValueError):
|
89
|
+
utils.get_template(space="mni152_2mm", mask="gm")
|
90
|
+
|
91
|
+
with pytest.raises(ValueError):
|
92
|
+
utils.get_template(space="ale_2mm", mask="gm")
|
93
|
+
|
94
|
+
|
95
|
+
def test_get_resource_path():
|
96
|
+
"""Test nimare.utils.get_resource_path."""
|
97
|
+
print(utils.get_resource_path())
|
98
|
+
assert op.isdir(utils.get_resource_path())
|
99
|
+
|
100
|
+
|
101
|
+
@pytest.mark.parametrize(
|
102
|
+
"has_low_memory,memory_limit",
|
103
|
+
[
|
104
|
+
(True, "1gb"),
|
105
|
+
(False, None),
|
106
|
+
],
|
107
|
+
)
|
108
|
+
def test_use_memmap(caplog, has_low_memory, memory_limit):
|
109
|
+
"""Test the memmapping decorator."""
|
110
|
+
LGR = logging.getLogger(__name__)
|
111
|
+
|
112
|
+
class DummyClass:
|
113
|
+
def __init__(self, has_low_memory, memory_limit):
|
114
|
+
self.has_low_memory = has_low_memory
|
115
|
+
self.memory_limit = memory_limit
|
116
|
+
|
117
|
+
@utils.use_memmap(LGR)
|
118
|
+
def test_decorator(self):
|
119
|
+
assert hasattr(self, "memmap_filenames")
|
120
|
+
if self.has_low_memory:
|
121
|
+
assert hasattr(self, "memory_limit")
|
122
|
+
if self.memory_limit:
|
123
|
+
assert os.path.isfile(self.memmap_filenames[0])
|
124
|
+
else:
|
125
|
+
assert self.memmap_filenames[0] is None
|
126
|
+
return self.memmap_filenames
|
127
|
+
|
128
|
+
@utils.use_memmap(LGR)
|
129
|
+
def bad_justin_timberlake(self):
|
130
|
+
raise ValueError("It's gonna be may!")
|
131
|
+
|
132
|
+
my_class = DummyClass(has_low_memory, memory_limit)
|
133
|
+
|
134
|
+
# make sure memmap file has been deleted
|
135
|
+
my_class.test_decorator()
|
136
|
+
first_memmap_filename = my_class.memmap_filenames[0]
|
137
|
+
|
138
|
+
# run bad function
|
139
|
+
with pytest.raises(ValueError):
|
140
|
+
my_class.bad_justin_timberlake()
|
141
|
+
assert "failed, removing" in caplog.text
|
142
|
+
|
143
|
+
if hasattr(my_class, "memory_limit") and my_class.memory_limit:
|
144
|
+
assert not os.path.isfile(first_memmap_filename)
|
145
|
+
assert not os.path.isfile(my_class.memmap_filenames[0])
|
146
|
+
# test when a function is called a new memmap file is created
|
147
|
+
assert first_memmap_filename != my_class.memmap_filenames[0]
|
148
|
+
|
149
|
+
|
150
|
+
def test_tal2mni():
|
151
|
+
"""TODO: Get converted coords from official site."""
|
152
|
+
test = np.array([[-44, 31, 27], [20, -32, 14], [28, -76, 28]])
|
153
|
+
true = np.array(
|
154
|
+
[
|
155
|
+
[-45.83997568, 35.97904559, 23.55194326],
|
156
|
+
[22.69248975, -31.34145016, 13.91284087],
|
157
|
+
[31.53113226, -76.61685748, 33.22105166],
|
158
|
+
]
|
159
|
+
)
|
160
|
+
assert np.allclose(utils.tal2mni(test), true)
|
161
|
+
|
162
|
+
|
163
|
+
def test_mni2tal():
|
164
|
+
"""TODO: Get converted coords from official site."""
|
165
|
+
test = np.array([[-44, 31, 27], [20, -32, 14], [28, -76, 28]])
|
166
|
+
true = np.array(
|
167
|
+
[[-42.3176, 26.0594, 29.7364], [17.4781, -32.6076, 14.0009], [24.7353, -75.0184, 23.3283]]
|
168
|
+
)
|
169
|
+
assert np.allclose(utils.mni2tal(test), true)
|
170
|
+
|
171
|
+
|
172
|
+
def test_vox2mm():
|
173
|
+
"""Test vox2mm."""
|
174
|
+
test = np.array([[20, 20, 20], [0, 0, 0]])
|
175
|
+
true = np.array([[-50.0, -86.0, -32.0], [-90.0, -126.0, -72.0]])
|
176
|
+
img = utils.get_template(space="mni152_2mm", mask=None)
|
177
|
+
aff = img.affine
|
178
|
+
assert np.array_equal(utils.vox2mm(test, aff), true)
|
179
|
+
|
180
|
+
|
181
|
+
def test_mm2vox():
|
182
|
+
"""Test mm2vox."""
|
183
|
+
test = np.array([[20, 20, 20], [0, 0, 0]])
|
184
|
+
true = np.array([[55.0, 73.0, 46.0], [45.0, 63.0, 36.0]])
|
185
|
+
img = utils.get_template(space="mni152_2mm", mask=None)
|
186
|
+
aff = img.affine
|
187
|
+
assert np.array_equal(utils.mm2vox(test, aff), true)
|
188
|
+
|
189
|
+
|
190
|
+
def test_apply_liberal_mask():
|
191
|
+
"""Test _apply_liberal_mask."""
|
192
|
+
data = np.array([[1, 2, np.nan, np.nan], [4, np.nan, 6, 5], [0, 8, 9, 3]])
|
193
|
+
true_data = [np.array([[1], [4]]), np.array([[2], [8]]), np.array([[6, 5], [9, 3]])]
|
194
|
+
|
195
|
+
pred_data, _, _ = _apply_liberal_mask(data)
|
196
|
+
|
197
|
+
assert len(pred_data) == len(true_data)
|
198
|
+
|
199
|
+
for pred_val, true_val in zip(pred_data, true_data):
|
200
|
+
assert np.array_equal(pred_val, true_val)
|
@@ -0,0 +1,221 @@
|
|
1
|
+
"""Test nimare.workflows."""
|
2
|
+
|
3
|
+
import os.path as op
|
4
|
+
|
5
|
+
import nibabel as nib
|
6
|
+
import numpy as np
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
import nimare
|
10
|
+
from nimare.correct import FWECorrector
|
11
|
+
from nimare.diagnostics import FocusCounter, Jackknife
|
12
|
+
from nimare.meta.cbma import ALE, ALESubtraction, MKDAChi2
|
13
|
+
from nimare.meta.ibma import Fishers, PermutedOLS, Stouffers
|
14
|
+
from nimare.workflows import (
|
15
|
+
CBMAWorkflow,
|
16
|
+
IBMAWorkflow,
|
17
|
+
PairwiseCBMAWorkflow,
|
18
|
+
conjunction_analysis,
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
@pytest.mark.parametrize(
|
23
|
+
"estimator,corrector,diagnostics",
|
24
|
+
[
|
25
|
+
(ALE, FWECorrector(method="montecarlo", n_iters=10), [Jackknife]),
|
26
|
+
("ales", "bonferroni", Jackknife),
|
27
|
+
("ale", "bonferroni", [Jackknife, FocusCounter]),
|
28
|
+
("kda", "fdr", Jackknife),
|
29
|
+
("mkdadensity", "fdr", "focuscounter"),
|
30
|
+
(MKDAChi2, "montecarlo", None),
|
31
|
+
(Fishers, "montecarlo", "jackknife"),
|
32
|
+
],
|
33
|
+
)
|
34
|
+
def test_cbma_workflow_smoke(
|
35
|
+
tmp_path_factory,
|
36
|
+
testdata_cbma_full,
|
37
|
+
estimator,
|
38
|
+
corrector,
|
39
|
+
diagnostics,
|
40
|
+
):
|
41
|
+
"""Run smoke test for CBMA workflow."""
|
42
|
+
tmpdir = tmp_path_factory.mktemp("test_cbma_workflow_smoke")
|
43
|
+
|
44
|
+
if estimator == MKDAChi2:
|
45
|
+
with pytest.raises(AttributeError):
|
46
|
+
CBMAWorkflow(estimator=estimator, corrector=corrector, diagnostics=diagnostics)
|
47
|
+
elif estimator == Fishers:
|
48
|
+
with pytest.raises((AttributeError, ValueError)):
|
49
|
+
CBMAWorkflow(estimator=estimator, corrector=corrector, diagnostics=diagnostics)
|
50
|
+
elif estimator == "ales":
|
51
|
+
with pytest.raises(ValueError):
|
52
|
+
CBMAWorkflow(estimator=estimator, corrector=corrector, diagnostics=diagnostics)
|
53
|
+
else:
|
54
|
+
workflow = CBMAWorkflow(
|
55
|
+
estimator=estimator,
|
56
|
+
corrector=corrector,
|
57
|
+
diagnostics=diagnostics,
|
58
|
+
output_dir=tmpdir,
|
59
|
+
)
|
60
|
+
cres = workflow.fit(testdata_cbma_full)
|
61
|
+
|
62
|
+
assert isinstance(cres, nimare.results.MetaResult)
|
63
|
+
assert op.isfile(op.join(tmpdir, "boilerplate.txt"))
|
64
|
+
assert op.isfile(op.join(tmpdir, "references.bib"))
|
65
|
+
|
66
|
+
for imgtype in cres.maps.keys():
|
67
|
+
filename = f"{imgtype}.nii.gz"
|
68
|
+
outpath = op.join(tmpdir, filename)
|
69
|
+
# For ALE maps are None
|
70
|
+
if not cres.maps[imgtype] is None:
|
71
|
+
assert op.isfile(outpath)
|
72
|
+
|
73
|
+
for tabletype in cres.tables.keys():
|
74
|
+
filename = f"{tabletype}.tsv"
|
75
|
+
outpath = op.join(tmpdir, filename)
|
76
|
+
# For ALE tables are None
|
77
|
+
if not cres.tables[tabletype] is None:
|
78
|
+
assert op.isfile(outpath)
|
79
|
+
|
80
|
+
|
81
|
+
@pytest.mark.parametrize(
|
82
|
+
"estimator,corrector,diagnostics",
|
83
|
+
[
|
84
|
+
(MKDAChi2, FWECorrector(method="montecarlo", n_iters=10), [FocusCounter]),
|
85
|
+
("mkdachi", "bonferroni", FocusCounter),
|
86
|
+
("mkdachi2", "bonferroni", "jackknife"),
|
87
|
+
(ALESubtraction(n_iters=10), "fdr", Jackknife(voxel_thresh=0.01)),
|
88
|
+
(ALE, "montecarlo", None),
|
89
|
+
(Fishers, "montecarlo", "jackknife"),
|
90
|
+
],
|
91
|
+
)
|
92
|
+
def test_pairwise_cbma_workflow_smoke(
|
93
|
+
tmp_path_factory,
|
94
|
+
testdata_cbma_full,
|
95
|
+
estimator,
|
96
|
+
corrector,
|
97
|
+
diagnostics,
|
98
|
+
):
|
99
|
+
"""Run smoke test for CBMA workflow."""
|
100
|
+
tmpdir = tmp_path_factory.mktemp("test_pairwise_cbma_workflow_smoke")
|
101
|
+
|
102
|
+
dset1 = testdata_cbma_full.slice(testdata_cbma_full.ids[:10])
|
103
|
+
dset2 = testdata_cbma_full.slice(testdata_cbma_full.ids[10:])
|
104
|
+
if estimator in [ALE, "mkdachi"]:
|
105
|
+
with pytest.raises(ValueError):
|
106
|
+
PairwiseCBMAWorkflow(estimator=estimator, corrector=corrector, diagnostics=diagnostics)
|
107
|
+
elif estimator == Fishers:
|
108
|
+
with pytest.raises((AttributeError, ValueError)):
|
109
|
+
PairwiseCBMAWorkflow(estimator=estimator, corrector=corrector, diagnostics=diagnostics)
|
110
|
+
else:
|
111
|
+
workflow = PairwiseCBMAWorkflow(
|
112
|
+
estimator=estimator,
|
113
|
+
corrector=corrector,
|
114
|
+
diagnostics=diagnostics,
|
115
|
+
output_dir=tmpdir,
|
116
|
+
)
|
117
|
+
cres = workflow.fit(dset1, dset2)
|
118
|
+
|
119
|
+
assert isinstance(cres, nimare.results.MetaResult)
|
120
|
+
assert op.isfile(op.join(tmpdir, "boilerplate.txt"))
|
121
|
+
assert op.isfile(op.join(tmpdir, "references.bib"))
|
122
|
+
|
123
|
+
for imgtype in cres.maps.keys():
|
124
|
+
filename = f"{imgtype}.nii.gz"
|
125
|
+
outpath = op.join(tmpdir, filename)
|
126
|
+
# For MKDAChi2 maps are None
|
127
|
+
if cres.maps[imgtype] is not None:
|
128
|
+
assert op.isfile(outpath)
|
129
|
+
|
130
|
+
for tabletype in cres.tables.keys():
|
131
|
+
filename = f"{tabletype}.tsv"
|
132
|
+
outpath = op.join(tmpdir, filename)
|
133
|
+
# For MKDAChi2 tables are None
|
134
|
+
if cres.tables[tabletype] is not None:
|
135
|
+
assert op.isfile(outpath)
|
136
|
+
|
137
|
+
|
138
|
+
@pytest.mark.parametrize(
|
139
|
+
"estimator,corrector,diagnostics",
|
140
|
+
[
|
141
|
+
(PermutedOLS, FWECorrector(method="montecarlo", n_iters=10), "jackknife"),
|
142
|
+
(Stouffers, "bonferroni", "jackknife"),
|
143
|
+
("fishers", "fdr", "jackknife"),
|
144
|
+
],
|
145
|
+
)
|
146
|
+
def test_ibma_workflow_smoke(
|
147
|
+
tmp_path_factory,
|
148
|
+
testdata_ibma,
|
149
|
+
estimator,
|
150
|
+
corrector,
|
151
|
+
diagnostics,
|
152
|
+
):
|
153
|
+
"""Run smoke test for CBMA workflow."""
|
154
|
+
tmpdir = tmp_path_factory.mktemp("test_ibma_workflow_smoke")
|
155
|
+
|
156
|
+
workflow = IBMAWorkflow(
|
157
|
+
estimator=estimator,
|
158
|
+
corrector=corrector,
|
159
|
+
diagnostics=diagnostics,
|
160
|
+
output_dir=tmpdir,
|
161
|
+
)
|
162
|
+
cres = workflow.fit(testdata_ibma)
|
163
|
+
|
164
|
+
assert isinstance(cres, nimare.results.MetaResult)
|
165
|
+
assert op.isfile(op.join(tmpdir, "boilerplate.txt"))
|
166
|
+
assert op.isfile(op.join(tmpdir, "references.bib"))
|
167
|
+
|
168
|
+
for imgtype in cres.maps.keys():
|
169
|
+
filename = f"{imgtype}.nii.gz"
|
170
|
+
outpath = op.join(tmpdir, filename)
|
171
|
+
assert op.isfile(outpath)
|
172
|
+
|
173
|
+
for tabletype in cres.tables.keys():
|
174
|
+
filename = f"{tabletype}.tsv"
|
175
|
+
outpath = op.join(tmpdir, filename)
|
176
|
+
assert op.isfile(outpath)
|
177
|
+
|
178
|
+
|
179
|
+
def test_conjunction_analysis_smoke(tmp_path_factory):
|
180
|
+
"""Run smoke test for conjunction analysis workflow."""
|
181
|
+
# Create two 3D arrays with random values
|
182
|
+
arr1 = np.random.rand(10, 10, 10)
|
183
|
+
arr2 = np.random.rand(10, 10, 10)
|
184
|
+
|
185
|
+
# Create two Nifti1Image objects from the arrays
|
186
|
+
img1 = nib.Nifti1Image(arr1, np.eye(4))
|
187
|
+
img2 = nib.Nifti1Image(arr2, np.eye(4))
|
188
|
+
|
189
|
+
# Perform conjunction analysis on the two images
|
190
|
+
conj_img = conjunction_analysis([img1, img2])
|
191
|
+
|
192
|
+
# Check that the output is a Nifti1Image object
|
193
|
+
assert isinstance(conj_img, nib.Nifti1Image)
|
194
|
+
|
195
|
+
# Check that the output has the same shape as the input images
|
196
|
+
assert conj_img.shape == img1.shape
|
197
|
+
|
198
|
+
# Check that the output has the correct values
|
199
|
+
expected_output = np.minimum.reduce([arr1, arr2])
|
200
|
+
np.testing.assert_array_equal(conj_img.get_fdata(), expected_output)
|
201
|
+
|
202
|
+
# Test passing in a list of strings
|
203
|
+
tmpdir = tmp_path_factory.mktemp("test_conjunction_analysis_smoke")
|
204
|
+
img1_fn = op.join(tmpdir, "image1.nii.gz")
|
205
|
+
img2_fn = op.join(tmpdir, "image2.nii.gz")
|
206
|
+
img1.to_filename(img1_fn)
|
207
|
+
img2.to_filename(img2_fn)
|
208
|
+
|
209
|
+
# Perform conjunction analysis on the two images from nifti files
|
210
|
+
conj_img_fromstr = conjunction_analysis([img1_fn, img2_fn])
|
211
|
+
|
212
|
+
# Check that the output has the correct values
|
213
|
+
np.testing.assert_array_equal(conj_img.get_fdata(), conj_img_fromstr.get_fdata())
|
214
|
+
|
215
|
+
# Raise error if only one image is provided
|
216
|
+
with pytest.raises(ValueError):
|
217
|
+
conjunction_analysis([img1])
|
218
|
+
|
219
|
+
# Raise error if invalid image type is provided
|
220
|
+
with pytest.raises(ValueError):
|
221
|
+
conjunction_analysis([1, 2])
|
nimare/tests/utils.py
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
"""Utility functions for testing nimare."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import os.path as op
|
5
|
+
from contextlib import ExitStack as does_not_raise
|
6
|
+
|
7
|
+
import nibabel as nib
|
8
|
+
import numpy as np
|
9
|
+
import pytest
|
10
|
+
|
11
|
+
from nimare.meta.utils import compute_kda_ma
|
12
|
+
|
13
|
+
# set significance levels used for testing.
|
14
|
+
# duplicated in test_estimator_performance
|
15
|
+
ALPHA = 0.05
|
16
|
+
|
17
|
+
LGR = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
def get_test_data_path():
|
21
|
+
"""Return the path to test datasets, terminated with separator.
|
22
|
+
|
23
|
+
Test-related data are kept in tests folder in "data".
|
24
|
+
Based on function by Yaroslav Halchenko used in Neurosynth Python package.
|
25
|
+
"""
|
26
|
+
return op.abspath(op.join(op.dirname(__file__), "data") + op.sep)
|
27
|
+
|
28
|
+
|
29
|
+
def _create_signal_mask(ground_truth_foci_ijks, mask):
|
30
|
+
"""Create binary images to identify areas of likely significance and nonsignificance.
|
31
|
+
|
32
|
+
Parameters
|
33
|
+
----------
|
34
|
+
ground_truth_foci_ijks : array_like
|
35
|
+
Ground truth ijk coordinates of foci.
|
36
|
+
mask : :obj:`nibabel.Nifti1Image`
|
37
|
+
Input mask to define shape and size of output binary masks
|
38
|
+
|
39
|
+
Returns
|
40
|
+
-------
|
41
|
+
sig_map : :obj:`nibabel.Nifti1Image`
|
42
|
+
Binary image representing regions around the
|
43
|
+
ground truth foci expected to be significant.
|
44
|
+
nonsig_map : :obj:`nibabel.Nifti1Image`
|
45
|
+
Binary image representing regions not expected
|
46
|
+
to be significant within the brain.
|
47
|
+
"""
|
48
|
+
# area where I'm reasonably certain there are significant results
|
49
|
+
sig_prob_map = compute_kda_ma(mask, ground_truth_foci_ijks, r=2, value=1, sum_overlap=False)
|
50
|
+
sig_prob_map = sig_prob_map[0].todense()
|
51
|
+
|
52
|
+
# area where I'm reasonably certain there are not significant results
|
53
|
+
nonsig_prob_map = compute_kda_ma(
|
54
|
+
mask, ground_truth_foci_ijks, r=14, value=1, sum_overlap=False
|
55
|
+
)
|
56
|
+
nonsig_prob_map = nonsig_prob_map[0].todense()
|
57
|
+
|
58
|
+
sig_map = nib.Nifti1Image((sig_prob_map == 1).astype(np.int32), affine=mask.affine)
|
59
|
+
nonsig_map = nib.Nifti1Image((nonsig_prob_map == 0).astype(np.int32), affine=mask.affine)
|
60
|
+
return sig_map, nonsig_map
|
61
|
+
|
62
|
+
|
63
|
+
def _check_p_values(
|
64
|
+
p_array,
|
65
|
+
masker,
|
66
|
+
sig_idx,
|
67
|
+
nonsig_idx,
|
68
|
+
alpha,
|
69
|
+
ground_truth_foci_ijks,
|
70
|
+
n_iters=None,
|
71
|
+
good_sensitivity=True,
|
72
|
+
good_specificity=True,
|
73
|
+
):
|
74
|
+
"""Check if p-values are within the correct range."""
|
75
|
+
################################################
|
76
|
+
# CHECK IF P-VALUES ARE WITHIN THE CORRECT RANGE
|
77
|
+
################################################
|
78
|
+
if n_iters:
|
79
|
+
assert p_array.min() >= (1.0 / n_iters)
|
80
|
+
assert p_array.max() <= 1.0 - (1.0 / n_iters)
|
81
|
+
else:
|
82
|
+
assert (p_array >= 0).all() and (p_array <= 1).all()
|
83
|
+
|
84
|
+
p_map = masker.inverse_transform(p_array).get_fdata()
|
85
|
+
|
86
|
+
# reformat coordinate indices to index p_map
|
87
|
+
gtf_idx = [
|
88
|
+
[ground_truth_foci_ijks[i][j] for i in range(len(ground_truth_foci_ijks))]
|
89
|
+
for j in range(3)
|
90
|
+
]
|
91
|
+
|
92
|
+
best_chance_p_values = p_map[tuple(gtf_idx)]
|
93
|
+
assert all(best_chance_p_values < ALPHA) == good_sensitivity
|
94
|
+
|
95
|
+
p_array_sig = p_array[sig_idx]
|
96
|
+
p_array_nonsig = p_array[nonsig_idx]
|
97
|
+
|
98
|
+
# assert that at least 50% of voxels surrounding the foci
|
99
|
+
# are significant at alpha = .05
|
100
|
+
observed_sig = p_array_sig < alpha
|
101
|
+
observed_sig_perc = observed_sig.sum() / len(observed_sig)
|
102
|
+
assert (observed_sig_perc >= 0.5) == good_sensitivity
|
103
|
+
|
104
|
+
# assert that more than 95% of voxels farther away
|
105
|
+
# from foci are nonsignificant at alpha = 0.05
|
106
|
+
observed_nonsig = p_array_nonsig > alpha
|
107
|
+
observed_nonsig_perc = observed_nonsig.sum() / len(observed_nonsig)
|
108
|
+
assert np.isclose(observed_nonsig_perc, (1 - alpha), atol=0.05) == good_specificity
|
109
|
+
|
110
|
+
|
111
|
+
def _transform_res(meta, meta_res, corr):
|
112
|
+
"""Evaluate whether meta estimator and corrector work together."""
|
113
|
+
#######################################
|
114
|
+
# CHECK IF META/CORRECTOR WORK TOGETHER
|
115
|
+
#######################################
|
116
|
+
# all combinations of meta-analysis estimators and multiple comparison correctors
|
117
|
+
# that do not work together
|
118
|
+
corr_expectation = does_not_raise()
|
119
|
+
|
120
|
+
with corr_expectation:
|
121
|
+
cres = corr.transform(meta_res)
|
122
|
+
|
123
|
+
# if multiple correction failed (expected) do not continue
|
124
|
+
if isinstance(corr_expectation, type(pytest.raises(ValueError))):
|
125
|
+
pytest.xfail("this meta-analysis & corrector combo fails")
|
126
|
+
return cres
|