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,298 @@
1
+ """Test nimare.meta.ale (ALE/SCALE meta-analytic algorithms)."""
2
+
3
+ import os
4
+ import pickle
5
+
6
+ import nibabel as nib
7
+ import numpy as np
8
+ import pytest
9
+ from nilearn.input_data import NiftiLabelsMasker
10
+
11
+ import nimare
12
+ from nimare.correct import FDRCorrector, FWECorrector
13
+ from nimare.meta import ale
14
+ from nimare.results import MetaResult
15
+ from nimare.tests.utils import get_test_data_path
16
+ from nimare.utils import vox2mm
17
+
18
+
19
+ def test_ALE_approximate_null_unit(testdata_cbma, tmp_path_factory):
20
+ """Unit test for ALE with approximate null_method."""
21
+ tmpdir = tmp_path_factory.mktemp("test_ALE_approximate_null_unit")
22
+ est_out_file = os.path.join(tmpdir, "est_file.pkl.gz")
23
+ res_out_file = os.path.join(tmpdir, "res_file.pkl.gz")
24
+
25
+ meta = ale.ALE(null_method="approximate")
26
+ results = meta.fit(testdata_cbma)
27
+ assert "stat" in results.maps.keys()
28
+ assert "p" in results.maps.keys()
29
+ assert "z" in results.maps.keys()
30
+ assert isinstance(results, nimare.results.MetaResult)
31
+ assert isinstance(results.get_map("z", return_type="image"), nib.Nifti1Image)
32
+ assert isinstance(results.get_map("z", return_type="array"), np.ndarray)
33
+ results_copy = results.copy()
34
+ assert results_copy != results
35
+ assert isinstance(results, nimare.results.MetaResult)
36
+
37
+ # Test saving/loading estimator
38
+ for compress in [True, False]:
39
+ meta.save(est_out_file, compress=compress)
40
+ assert os.path.isfile(est_out_file)
41
+ meta2 = ale.ALE.load(est_out_file, compressed=compress)
42
+ assert isinstance(meta2, ale.ALE)
43
+ if compress:
44
+ with pytest.raises(pickle.UnpicklingError):
45
+ ale.ALE.load(est_out_file, compressed=(not compress))
46
+ else:
47
+ with pytest.raises(OSError):
48
+ ale.ALE.load(est_out_file, compressed=(not compress))
49
+
50
+ # Test saving/loading MetaResult object
51
+ for compress in [True, False]:
52
+ results.save(res_out_file, compress=compress)
53
+ assert os.path.isfile(res_out_file)
54
+ res2 = MetaResult.load(res_out_file, compressed=compress)
55
+ assert isinstance(res2, MetaResult)
56
+ if compress:
57
+ with pytest.raises(pickle.UnpicklingError):
58
+ MetaResult.load(res_out_file, compressed=(not compress))
59
+ else:
60
+ with pytest.raises(OSError):
61
+ MetaResult.load(res_out_file, compressed=(not compress))
62
+
63
+ # Test MCC methods
64
+ # Monte Carlo FWE
65
+ corr = FWECorrector(method="montecarlo", voxel_thresh=0.001, n_iters=5, n_cores=-1)
66
+ corr_results = corr.transform(results)
67
+ assert isinstance(corr_results, nimare.results.MetaResult)
68
+ assert isinstance(corr_results.description_, str)
69
+ assert "z_desc-size_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
70
+ assert "z_desc-mass_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
71
+ assert "z_level-voxel_corr-FWE_method-montecarlo" in corr_results.maps.keys()
72
+ assert "logp_desc-size_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
73
+ assert "logp_desc-mass_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
74
+ assert "logp_level-voxel_corr-FWE_method-montecarlo" in corr_results.maps.keys()
75
+ assert isinstance(
76
+ corr_results.get_map(
77
+ "z_desc-size_level-cluster_corr-FWE_method-montecarlo", return_type="image"
78
+ ),
79
+ nib.Nifti1Image,
80
+ )
81
+ assert isinstance(
82
+ corr_results.get_map(
83
+ "z_desc-size_level-cluster_corr-FWE_method-montecarlo", return_type="array"
84
+ ),
85
+ np.ndarray,
86
+ )
87
+ assert isinstance(
88
+ corr_results.get_map(
89
+ "z_desc-mass_level-cluster_corr-FWE_method-montecarlo", return_type="image"
90
+ ),
91
+ nib.Nifti1Image,
92
+ )
93
+ assert isinstance(
94
+ corr_results.get_map(
95
+ "z_desc-mass_level-cluster_corr-FWE_method-montecarlo", return_type="array"
96
+ ),
97
+ np.ndarray,
98
+ )
99
+
100
+ # Bonferroni FWE
101
+ corr = FWECorrector(method="bonferroni")
102
+ corr_results = corr.transform(results)
103
+ assert isinstance(corr_results, nimare.results.MetaResult)
104
+ assert isinstance(corr_results.description_, str)
105
+ assert isinstance(
106
+ corr_results.get_map("z_corr-FWE_method-bonferroni", return_type="image"), nib.Nifti1Image
107
+ )
108
+ assert isinstance(
109
+ corr_results.get_map("z_corr-FWE_method-bonferroni", return_type="array"), np.ndarray
110
+ )
111
+
112
+ # FDR
113
+ corr = FDRCorrector(method="indep", alpha=0.05)
114
+ corr_results = corr.transform(results)
115
+ assert isinstance(corr_results, nimare.results.MetaResult)
116
+ assert isinstance(corr_results.description_, str)
117
+ assert isinstance(
118
+ corr_results.get_map("z_corr-FDR_method-indep", return_type="image"), nib.Nifti1Image
119
+ )
120
+ assert isinstance(
121
+ corr_results.get_map("z_corr-FDR_method-indep", return_type="array"), np.ndarray
122
+ )
123
+
124
+
125
+ def test_ALE_montecarlo_null_unit(testdata_cbma, tmp_path_factory):
126
+ """Unit test for ALE with an montecarlo null_method.
127
+
128
+ This test is run with low-memory kernel transformation as well.
129
+ """
130
+ tmpdir = tmp_path_factory.mktemp("test_ALE_montecarlo_null_unit")
131
+ out_file = os.path.join(tmpdir, "file.pkl.gz")
132
+
133
+ meta = ale.ALE(null_method="montecarlo", n_iters=10)
134
+ results = meta.fit(testdata_cbma)
135
+ assert isinstance(results.description_, str)
136
+ assert "stat" in results.maps.keys()
137
+ assert "p" in results.maps.keys()
138
+ assert "z" in results.maps.keys()
139
+ assert isinstance(results, nimare.results.MetaResult)
140
+ assert isinstance(results.get_map("z", return_type="image"), nib.Nifti1Image)
141
+ assert isinstance(results.get_map("z", return_type="array"), np.ndarray)
142
+ results_copy = results.copy()
143
+ assert results_copy != results
144
+ assert isinstance(results, nimare.results.MetaResult)
145
+
146
+ # Test saving/loading
147
+ meta.save(out_file, compress=True)
148
+ assert os.path.isfile(out_file)
149
+ meta2 = ale.ALE.load(out_file, compressed=True)
150
+ assert isinstance(meta2, ale.ALE)
151
+ with pytest.raises(pickle.UnpicklingError):
152
+ ale.ALE.load(out_file, compressed=False)
153
+
154
+ meta.save(out_file, compress=False)
155
+ assert os.path.isfile(out_file)
156
+ meta2 = ale.ALE.load(out_file, compressed=False)
157
+ assert isinstance(meta2, ale.ALE)
158
+ with pytest.raises(OSError):
159
+ ale.ALE.load(out_file, compressed=True)
160
+
161
+ # Test MCC methods
162
+ # Monte Carlo FWE
163
+ corr = FWECorrector(method="montecarlo", voxel_thresh=0.001, n_iters=5, n_cores=-1)
164
+ corr_results = corr.transform(results)
165
+ assert isinstance(corr_results, nimare.results.MetaResult)
166
+ assert isinstance(corr_results.description_, str)
167
+ assert "z_desc-size_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
168
+ assert "z_desc-mass_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
169
+ assert "z_level-voxel_corr-FWE_method-montecarlo" in corr_results.maps.keys()
170
+ assert "logp_desc-size_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
171
+ assert "logp_desc-mass_level-cluster_corr-FWE_method-montecarlo" in corr_results.maps.keys()
172
+ assert "logp_level-voxel_corr-FWE_method-montecarlo" in corr_results.maps.keys()
173
+ assert isinstance(
174
+ corr_results.get_map(
175
+ "z_desc-size_level-cluster_corr-FWE_method-montecarlo", return_type="image"
176
+ ),
177
+ nib.Nifti1Image,
178
+ )
179
+ assert isinstance(
180
+ corr_results.get_map(
181
+ "z_desc-size_level-cluster_corr-FWE_method-montecarlo", return_type="array"
182
+ ),
183
+ np.ndarray,
184
+ )
185
+ assert isinstance(
186
+ corr_results.get_map(
187
+ "z_desc-mass_level-cluster_corr-FWE_method-montecarlo", return_type="image"
188
+ ),
189
+ nib.Nifti1Image,
190
+ )
191
+ assert isinstance(
192
+ corr_results.get_map(
193
+ "z_desc-mass_level-cluster_corr-FWE_method-montecarlo", return_type="array"
194
+ ),
195
+ np.ndarray,
196
+ )
197
+
198
+ # Check that the updated null distribution is in the corrected MetaResult's Estimator.
199
+ assert (
200
+ "values_desc-mass_level-cluster_corr-fwe_method-montecarlo"
201
+ in corr_results.estimator.null_distributions_.keys()
202
+ )
203
+ # The updated null distribution should *not* be in the original Estimator, nor in the
204
+ # uncorrected MetaResult's Estimator.
205
+ assert (
206
+ "values_desc-mass_level-cluster_corr-fwe_method-montecarlo"
207
+ not in meta.null_distributions_.keys()
208
+ )
209
+ assert (
210
+ "values_desc-mass_level-cluster_corr-fwe_method-montecarlo"
211
+ not in results.estimator.null_distributions_.keys()
212
+ )
213
+
214
+ # Bonferroni FWE
215
+ corr = FWECorrector(method="bonferroni")
216
+ corr_results = corr.transform(results)
217
+ assert isinstance(corr_results, nimare.results.MetaResult)
218
+ assert isinstance(
219
+ corr_results.get_map("z_corr-FWE_method-bonferroni", return_type="image"), nib.Nifti1Image
220
+ )
221
+ assert isinstance(
222
+ corr_results.get_map("z_corr-FWE_method-bonferroni", return_type="array"), np.ndarray
223
+ )
224
+
225
+ # FDR
226
+ corr = FDRCorrector(method="indep", alpha=0.05)
227
+ corr_results = corr.transform(results)
228
+ assert isinstance(corr_results, nimare.results.MetaResult)
229
+ assert isinstance(
230
+ corr_results.get_map("z_corr-FDR_method-indep", return_type="image"), nib.Nifti1Image
231
+ )
232
+ assert isinstance(
233
+ corr_results.get_map("z_corr-FDR_method-indep", return_type="array"), np.ndarray
234
+ )
235
+
236
+
237
+ def test_ALESubtraction_smoke(testdata_cbma, tmp_path_factory):
238
+ """Smoke test for ALESubtraction."""
239
+ tmpdir = tmp_path_factory.mktemp("test_ALESubtraction_smoke")
240
+ out_file = os.path.join(tmpdir, "file.pkl.gz")
241
+
242
+ sub_meta = ale.ALESubtraction(n_iters=10, n_cores=2)
243
+ results = sub_meta.fit(testdata_cbma, testdata_cbma)
244
+ assert isinstance(results, nimare.results.MetaResult)
245
+ assert isinstance(results.description_, str)
246
+ assert "z_desc-group1MinusGroup2" in results.maps.keys()
247
+ assert isinstance(
248
+ results.get_map("z_desc-group1MinusGroup2", return_type="image"), nib.Nifti1Image
249
+ )
250
+ assert isinstance(results.get_map("z_desc-group1MinusGroup2", return_type="array"), np.ndarray)
251
+
252
+ sub_meta.save(out_file)
253
+ assert os.path.isfile(out_file)
254
+
255
+
256
+ def test_SCALE_smoke(testdata_cbma, tmp_path_factory):
257
+ """Smoke test for SCALE."""
258
+ tmpdir = tmp_path_factory.mktemp("test_SCALE_smoke")
259
+ out_file = os.path.join(tmpdir, "file.pkl.gz")
260
+ dset = testdata_cbma.slice(testdata_cbma.ids[:3])
261
+
262
+ with pytest.raises(TypeError):
263
+ ale.SCALE(xyz="dog", n_iters=5, n_cores=1)
264
+
265
+ with pytest.raises(ValueError):
266
+ ale.SCALE(xyz=np.random.random((5, 3, 1)), n_iters=5, n_cores=1)
267
+
268
+ with pytest.raises(ValueError):
269
+ ale.SCALE(xyz=np.random.random((3, 10)), n_iters=5, n_cores=1)
270
+
271
+ xyz = vox2mm(
272
+ np.vstack(np.where(testdata_cbma.masker.mask_img.get_fdata())).T,
273
+ testdata_cbma.masker.mask_img.affine,
274
+ )
275
+ xyz = xyz[:20, :]
276
+ meta = ale.SCALE(xyz, n_iters=5, n_cores=1)
277
+ results = meta.fit(dset)
278
+ assert isinstance(results, nimare.results.MetaResult)
279
+ assert isinstance(results.description_, str)
280
+ assert "z" in results.maps.keys()
281
+ assert isinstance(results.get_map("z", return_type="image"), nib.Nifti1Image)
282
+ assert isinstance(results.get_map("z", return_type="array"), np.ndarray)
283
+
284
+ meta.save(out_file)
285
+ assert os.path.isfile(out_file)
286
+
287
+
288
+ def test_ALE_non_nifti_masker(testdata_cbma):
289
+ """Unit test for ALE with non-NiftiMasker.
290
+
291
+ CBMA estimators don't work with non-NiftiMasker (e.g., a NiftiLabelsMasker).
292
+ """
293
+ atlas = os.path.join(get_test_data_path(), "test_pain_dataset", "atlas.nii.gz")
294
+ masker = NiftiLabelsMasker(atlas)
295
+ meta = ale.ALE(mask=masker, n_iters=10)
296
+
297
+ with pytest.raises(ValueError):
298
+ meta.fit(testdata_cbma)
@@ -0,0 +1,295 @@
1
+ """Tests for CBMR meta-analytic methods."""
2
+
3
+ import logging
4
+ import warnings
5
+
6
+ import pytest
7
+
8
+ try:
9
+ import torch
10
+ except ImportError:
11
+ warnings.warn("Torch not installed. CBMR tests will be skipped.")
12
+ TORCH_INSTALLED = False
13
+ else:
14
+ TORCH_INSTALLED = True
15
+ from nimare.meta import models
16
+ from nimare.meta.cbmr import CBMREstimator, CBMRInference
17
+
18
+ import nimare
19
+ from nimare.correct import FDRCorrector, FWECorrector
20
+ from nimare.transforms import StandardizeField
21
+
22
+ # numba has a lot of debug messages that are not useful for testing
23
+ logging.getLogger("numba").setLevel(logging.WARNING)
24
+ # indexed_gzip has a few debug messages that are not useful for testing
25
+ logging.getLogger("indexed_gzip").setLevel(logging.WARNING)
26
+
27
+
28
+ if TORCH_INSTALLED:
29
+
30
+ @pytest.fixture(
31
+ scope="session",
32
+ params=[
33
+ pytest.param(models.PoissonEstimator, id="Poisson"),
34
+ pytest.param(models.NegativeBinomialEstimator, id="NegativeBinomial"),
35
+ pytest.param(
36
+ models.ClusteredNegativeBinomialEstimator, id="ClusteredNegativeBinomial"
37
+ ),
38
+ ],
39
+ )
40
+ def model(request):
41
+ """CBMR models."""
42
+ return request.param
43
+
44
+ else:
45
+ model = None
46
+
47
+
48
+ @pytest.fixture(scope="session")
49
+ def cbmr_result(testdata_cbmr_simulated, model):
50
+ """Test CBMR estimator."""
51
+ dset = StandardizeField(fields=["sample_sizes", "avg_age", "schizophrenia_subtype"]).transform(
52
+ testdata_cbmr_simulated
53
+ )
54
+
55
+ cbmr = CBMREstimator(
56
+ group_categories=["diagnosis", "drug_status"],
57
+ moderators=["standardized_sample_sizes", "standardized_avg_age", "schizophrenia_subtype"],
58
+ spline_spacing=100,
59
+ model=model,
60
+ penalty=False,
61
+ n_iter=1000,
62
+ lr=1,
63
+ tol=1e4,
64
+ device="cpu",
65
+ )
66
+ res = cbmr.fit(dataset=dset)
67
+ assert isinstance(res, nimare.results.MetaResult)
68
+ assert isinstance(res.description_, str)
69
+
70
+ return res
71
+
72
+
73
+ @pytest.fixture(scope="session")
74
+ def inference_results(testdata_cbmr_simulated, cbmr_result):
75
+ """Test inference results for CBMR estimator."""
76
+ inference = CBMRInference(device="cpu")
77
+ inference.fit(cbmr_result)
78
+ t_con_groups = inference.create_contrast(
79
+ [
80
+ "DepressionYes-DepressionNo",
81
+ ],
82
+ source="groups",
83
+ )
84
+ t_con_moderators = inference.create_contrast(
85
+ ["standardized_sample_sizes-standardized_avg_age"],
86
+ source="moderators",
87
+ )
88
+ contrast_result = inference.transform(
89
+ t_con_groups=t_con_groups, t_con_moderators=t_con_moderators
90
+ )
91
+
92
+ return contrast_result
93
+
94
+
95
+ @pytest.fixture(
96
+ scope="session",
97
+ params=[
98
+ pytest.param(FWECorrector(method="bonferroni"), id="bonferroni"),
99
+ pytest.param(FDRCorrector(method="indep"), id="indep"),
100
+ pytest.param(FDRCorrector(method="negcorr"), id="negcorr"),
101
+ ],
102
+ )
103
+ def corrector(request):
104
+ """Corrector classes."""
105
+ return request.param
106
+
107
+
108
+ def test_cbmr_estimator(cbmr_result):
109
+ """Unit test for CBMR estimator."""
110
+ assert isinstance(cbmr_result, nimare.results.MetaResult)
111
+
112
+
113
+ def test_cbmr_inference(inference_results):
114
+ """Unit test for CBMR inference."""
115
+ assert isinstance(inference_results, nimare.results.MetaResult)
116
+
117
+
118
+ def test_cbmr_correctors(inference_results, corrector):
119
+ """Unit test for Correctors that work with CBMR."""
120
+ corrected_results = corrector.transform(inference_results)
121
+ assert isinstance(corrected_results, nimare.results.MetaResult)
122
+
123
+
124
+ def test_firth_penalty(testdata_cbmr_simulated):
125
+ """Unit test for Firth penalty."""
126
+ dset = StandardizeField(fields=["sample_sizes", "avg_age", "schizophrenia_subtype"]).transform(
127
+ testdata_cbmr_simulated
128
+ )
129
+ cbmr = CBMREstimator(
130
+ group_categories=["diagnosis", "drug_status"],
131
+ moderators=["standardized_sample_sizes", "standardized_avg_age", "schizophrenia_subtype"],
132
+ spline_spacing=100,
133
+ model=models.PoissonEstimator,
134
+ penalty=True,
135
+ lr=1,
136
+ tol=1e4,
137
+ device="cpu",
138
+ )
139
+ res = cbmr.fit(dataset=dset)
140
+ assert isinstance(res, nimare.results.MetaResult)
141
+
142
+
143
+ def test_moderators_none(testdata_cbmr_simulated):
144
+ """Unit test for Firth penalty."""
145
+ dset = StandardizeField(fields=["sample_sizes", "avg_age", "schizophrenia_subtype"]).transform(
146
+ testdata_cbmr_simulated
147
+ )
148
+ cbmr = CBMREstimator(
149
+ group_categories=["diagnosis", "drug_status"],
150
+ moderators=None,
151
+ spline_spacing=100,
152
+ model=models.PoissonEstimator,
153
+ penalty=False,
154
+ lr=1,
155
+ tol=1e4,
156
+ device="cpu",
157
+ )
158
+ res = cbmr.fit(dataset=dset)
159
+ assert isinstance(res, nimare.results.MetaResult)
160
+ inference = CBMRInference(device="cpu")
161
+ inference.fit(res)
162
+
163
+ t_con_groups = inference.create_contrast(
164
+ [
165
+ "DepressionYes",
166
+ ],
167
+ source="groups",
168
+ )
169
+ inference_results = inference.transform(t_con_groups=t_con_groups)
170
+
171
+ assert isinstance(inference_results, nimare.results.MetaResult)
172
+
173
+
174
+ def test_CBMREstimator_update(testdata_cbmr_simulated):
175
+ """Unit test for CBMR estimator update function."""
176
+ testdata_cbmr_simulated = StandardizeField(
177
+ fields=["sample_sizes", "avg_age", "schizophrenia_subtype"]
178
+ ).transform(testdata_cbmr_simulated)
179
+ cbmr = CBMREstimator(
180
+ moderators=["standardized_sample_sizes", "standardized_avg_age", "schizophrenia_subtype"],
181
+ model=models.PoissonEstimator,
182
+ lr=1,
183
+ )
184
+
185
+ cbmr._collect_inputs(testdata_cbmr_simulated, drop_invalid=True)
186
+ cbmr._preprocess_input(testdata_cbmr_simulated)
187
+
188
+ # fit the model
189
+ init_weight_kwargs = {
190
+ "groups": cbmr.groups,
191
+ "moderators": cbmr.moderators,
192
+ "spatial_coef_dim": cbmr.inputs_["coef_spline_bases"].shape[1],
193
+ "moderators_coef_dim": len(cbmr.moderators) if cbmr.moderators else None,
194
+ }
195
+
196
+ cbmr.model.init_weights(**init_weight_kwargs)
197
+ optimizer = torch.optim.LBFGS(cbmr.model.parameters(), cbmr.lr)
198
+ # load dataset info to torch.tensor
199
+ if cbmr.moderators:
200
+ moderators_by_group_tensor = dict()
201
+ for group in cbmr.model.groups:
202
+ moderators_tensor = torch.tensor(
203
+ cbmr.inputs_["moderators_by_group"][group],
204
+ dtype=torch.float64,
205
+ device=cbmr.device,
206
+ )
207
+ moderators_by_group_tensor[group] = moderators_tensor
208
+ else:
209
+ moderators_by_group_tensor = None
210
+ foci_per_voxel_tensor, foci_per_study_tensor = dict(), dict()
211
+ for group in cbmr.model.groups:
212
+ group_foci_per_voxel_tensor = torch.tensor(
213
+ cbmr.inputs_["foci_per_voxel"][group], dtype=torch.float64, device=cbmr.device
214
+ )
215
+ group_foci_per_study_tensor = torch.tensor(
216
+ cbmr.inputs_["foci_per_study"][group], dtype=torch.float64, device=cbmr.device
217
+ )
218
+ foci_per_voxel_tensor[group] = group_foci_per_voxel_tensor
219
+ foci_per_study_tensor[group] = group_foci_per_study_tensor
220
+
221
+ if cbmr.iter == 0:
222
+ prev_loss = torch.tensor(float("inf")) # initialization loss difference
223
+
224
+ cbmr.model._update(
225
+ optimizer,
226
+ torch.tensor(cbmr.inputs_["coef_spline_bases"], dtype=torch.float64, device=cbmr.device),
227
+ moderators_by_group_tensor,
228
+ foci_per_voxel_tensor,
229
+ foci_per_study_tensor,
230
+ prev_loss,
231
+ )
232
+ # deliberately set the first spatial coefficient to nan
233
+ for group in cbmr.model.groups:
234
+ nan_coef = torch.tensor(cbmr.model.spatial_coef_linears[group].weight)
235
+ nan_coef[:, 0] = float("nan")
236
+ cbmr.model.spatial_coef_linears[group].weight = torch.nn.Parameter(nan_coef)
237
+
238
+ # Expect exceptions when one of the spatial coefficients is nan.
239
+ with pytest.raises(ValueError):
240
+ cbmr.model._update(
241
+ optimizer,
242
+ torch.tensor(
243
+ cbmr.inputs_["coef_spline_bases"], dtype=torch.float64, device=cbmr.device
244
+ ),
245
+ moderators_by_group_tensor,
246
+ foci_per_voxel_tensor,
247
+ foci_per_study_tensor,
248
+ prev_loss,
249
+ )
250
+
251
+
252
+ def test_StandardizeField(testdata_cbmr_simulated):
253
+ """Unit test for StandardizeField."""
254
+ dset = StandardizeField(fields=["sample_sizes", "avg_age"]).transform(testdata_cbmr_simulated)
255
+ assert isinstance(dset, nimare.dataset.Dataset)
256
+ assert "standardized_sample_sizes" in dset.annotations
257
+ assert "standardized_avg_age" in dset.annotations
258
+ assert dset.annotations["standardized_sample_sizes"].mean() == pytest.approx(0.0, abs=1e-3)
259
+ assert dset.annotations["standardized_sample_sizes"].std() == pytest.approx(1.0, abs=1e-3)
260
+ assert dset.annotations["standardized_avg_age"].mean() == pytest.approx(0.0, abs=1e-3)
261
+ assert dset.annotations["standardized_avg_age"].std() == pytest.approx(1.0, abs=1e-3)
262
+
263
+
264
+ @pytest.mark.cbmr_importerror
265
+ def test_cbmr_importerror():
266
+ """Test that ImportErrors are raised when torch is not installed."""
267
+ with pytest.raises(ImportError):
268
+ from nimare.meta.cbmr import CBMREstimator
269
+
270
+ CBMREstimator()
271
+
272
+ with pytest.raises(ImportError):
273
+ from nimare.meta.cbmr import CBMRInference
274
+
275
+ CBMRInference()
276
+
277
+ with pytest.raises(ImportError):
278
+ from nimare.meta.models import GeneralLinearModelEstimator
279
+
280
+ GeneralLinearModelEstimator()
281
+
282
+ with pytest.raises(ImportError):
283
+ from nimare.meta.models import PoissonEstimator
284
+
285
+ PoissonEstimator()
286
+
287
+ with pytest.raises(ImportError):
288
+ from nimare.meta.models import NegativeBinomialEstimator
289
+
290
+ NegativeBinomialEstimator()
291
+
292
+ with pytest.raises(ImportError):
293
+ from nimare.meta.models import ClusteredNegativeBinomialEstimator
294
+
295
+ ClusteredNegativeBinomialEstimator()