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.
Files changed (119) hide show
  1. benchmarks/__init__.py +0 -0
  2. benchmarks/bench_cbma.py +57 -0
  3. nimare/__init__.py +45 -0
  4. nimare/_version.py +21 -0
  5. nimare/annotate/__init__.py +21 -0
  6. nimare/annotate/cogat.py +213 -0
  7. nimare/annotate/gclda.py +924 -0
  8. nimare/annotate/lda.py +147 -0
  9. nimare/annotate/text.py +75 -0
  10. nimare/annotate/utils.py +87 -0
  11. nimare/base.py +217 -0
  12. nimare/cli.py +124 -0
  13. nimare/correct.py +462 -0
  14. nimare/dataset.py +685 -0
  15. nimare/decode/__init__.py +33 -0
  16. nimare/decode/base.py +115 -0
  17. nimare/decode/continuous.py +462 -0
  18. nimare/decode/discrete.py +753 -0
  19. nimare/decode/encode.py +110 -0
  20. nimare/decode/utils.py +44 -0
  21. nimare/diagnostics.py +510 -0
  22. nimare/estimator.py +139 -0
  23. nimare/extract/__init__.py +19 -0
  24. nimare/extract/extract.py +466 -0
  25. nimare/extract/utils.py +295 -0
  26. nimare/generate.py +331 -0
  27. nimare/io.py +667 -0
  28. nimare/meta/__init__.py +39 -0
  29. nimare/meta/cbma/__init__.py +6 -0
  30. nimare/meta/cbma/ale.py +951 -0
  31. nimare/meta/cbma/base.py +947 -0
  32. nimare/meta/cbma/mkda.py +1361 -0
  33. nimare/meta/cbmr.py +970 -0
  34. nimare/meta/ibma.py +1683 -0
  35. nimare/meta/kernel.py +501 -0
  36. nimare/meta/models.py +1199 -0
  37. nimare/meta/utils.py +494 -0
  38. nimare/nimads.py +492 -0
  39. nimare/reports/__init__.py +24 -0
  40. nimare/reports/base.py +664 -0
  41. nimare/reports/default.yml +123 -0
  42. nimare/reports/figures.py +651 -0
  43. nimare/reports/report.tpl +160 -0
  44. nimare/resources/__init__.py +1 -0
  45. nimare/resources/atlases/Harvard-Oxford-LICENSE +93 -0
  46. nimare/resources/atlases/HarvardOxford-cort-maxprob-thr25-2mm.nii.gz +0 -0
  47. nimare/resources/database_file_manifest.json +142 -0
  48. nimare/resources/english_spellings.csv +1738 -0
  49. nimare/resources/filenames.json +32 -0
  50. nimare/resources/neurosynth_laird_studies.json +58773 -0
  51. nimare/resources/neurosynth_stoplist.txt +396 -0
  52. nimare/resources/nidm_pain_dset.json +1349 -0
  53. nimare/resources/references.bib +541 -0
  54. nimare/resources/semantic_knowledge_children.txt +325 -0
  55. nimare/resources/semantic_relatedness_children.txt +249 -0
  56. nimare/resources/templates/MNI152_2x2x2_brainmask.nii.gz +0 -0
  57. nimare/resources/templates/tpl-MNI152NLin6Asym_res-01_T1w.nii.gz +0 -0
  58. nimare/resources/templates/tpl-MNI152NLin6Asym_res-01_desc-brain_mask.nii.gz +0 -0
  59. nimare/resources/templates/tpl-MNI152NLin6Asym_res-02_T1w.nii.gz +0 -0
  60. nimare/resources/templates/tpl-MNI152NLin6Asym_res-02_desc-brain_mask.nii.gz +0 -0
  61. nimare/results.py +225 -0
  62. nimare/stats.py +276 -0
  63. nimare/tests/__init__.py +1 -0
  64. nimare/tests/conftest.py +229 -0
  65. nimare/tests/data/amygdala_roi.nii.gz +0 -0
  66. nimare/tests/data/data-neurosynth_version-7_coordinates.tsv.gz +0 -0
  67. nimare/tests/data/data-neurosynth_version-7_metadata.tsv.gz +0 -0
  68. nimare/tests/data/data-neurosynth_version-7_vocab-terms_source-abstract_type-tfidf_features.npz +0 -0
  69. nimare/tests/data/data-neurosynth_version-7_vocab-terms_vocabulary.txt +100 -0
  70. nimare/tests/data/neurosynth_dset.json +2868 -0
  71. nimare/tests/data/neurosynth_laird_studies.json +58773 -0
  72. nimare/tests/data/nidm_pain_dset.json +1349 -0
  73. nimare/tests/data/nimads_annotation.json +1 -0
  74. nimare/tests/data/nimads_studyset.json +1 -0
  75. nimare/tests/data/test_baseline.txt +2 -0
  76. nimare/tests/data/test_pain_dataset.json +1278 -0
  77. nimare/tests/data/test_pain_dataset_multiple_contrasts.json +1242 -0
  78. nimare/tests/data/test_sleuth_file.txt +18 -0
  79. nimare/tests/data/test_sleuth_file2.txt +10 -0
  80. nimare/tests/data/test_sleuth_file3.txt +5 -0
  81. nimare/tests/data/test_sleuth_file4.txt +5 -0
  82. nimare/tests/data/test_sleuth_file5.txt +5 -0
  83. nimare/tests/test_annotate_cogat.py +32 -0
  84. nimare/tests/test_annotate_gclda.py +86 -0
  85. nimare/tests/test_annotate_lda.py +27 -0
  86. nimare/tests/test_dataset.py +99 -0
  87. nimare/tests/test_decode_continuous.py +132 -0
  88. nimare/tests/test_decode_discrete.py +92 -0
  89. nimare/tests/test_diagnostics.py +168 -0
  90. nimare/tests/test_estimator_performance.py +385 -0
  91. nimare/tests/test_extract.py +46 -0
  92. nimare/tests/test_generate.py +247 -0
  93. nimare/tests/test_io.py +294 -0
  94. nimare/tests/test_meta_ale.py +298 -0
  95. nimare/tests/test_meta_cbmr.py +295 -0
  96. nimare/tests/test_meta_ibma.py +240 -0
  97. nimare/tests/test_meta_kernel.py +209 -0
  98. nimare/tests/test_meta_mkda.py +234 -0
  99. nimare/tests/test_nimads.py +21 -0
  100. nimare/tests/test_reports.py +110 -0
  101. nimare/tests/test_stats.py +101 -0
  102. nimare/tests/test_transforms.py +272 -0
  103. nimare/tests/test_utils.py +200 -0
  104. nimare/tests/test_workflows.py +221 -0
  105. nimare/tests/utils.py +126 -0
  106. nimare/transforms.py +907 -0
  107. nimare/utils.py +1367 -0
  108. nimare/workflows/__init__.py +14 -0
  109. nimare/workflows/base.py +189 -0
  110. nimare/workflows/cbma.py +165 -0
  111. nimare/workflows/ibma.py +108 -0
  112. nimare/workflows/macm.py +77 -0
  113. nimare/workflows/misc.py +65 -0
  114. nimare-0.4.2.dist-info/LICENSE +21 -0
  115. nimare-0.4.2.dist-info/METADATA +124 -0
  116. nimare-0.4.2.dist-info/RECORD +119 -0
  117. nimare-0.4.2.dist-info/WHEEL +5 -0
  118. nimare-0.4.2.dist-info/entry_points.txt +2 -0
  119. 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