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,651 @@
1
+ """Plot figures for report."""
2
+
3
+ import matplotlib.colors as mcolors
4
+ import matplotlib.patches as mpatches
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ import pandas as pd
8
+ import plotly.express as px
9
+ from nilearn import datasets
10
+ from nilearn.plotting import (
11
+ plot_connectome,
12
+ plot_img,
13
+ plot_roi,
14
+ plot_stat_map,
15
+ view_connectome,
16
+ view_img,
17
+ )
18
+ from ridgeplot import ridgeplot
19
+ from scipy import stats
20
+ from scipy.cluster.hierarchy import leaves_list, linkage, optimal_leaf_ordering
21
+
22
+ TABLE_STYLE = [
23
+ dict(
24
+ selector="th, td",
25
+ props=[
26
+ ("text-align", "center"),
27
+ ("font-family", "monospace"),
28
+ ("font-size", "15px"),
29
+ ("padding", "5px 3px"),
30
+ ("margin", "0px 3px"),
31
+ ("border", "1px solid #ddd"),
32
+ ],
33
+ ),
34
+ ]
35
+
36
+
37
+ PXS_PER_STD = 30 # Number of pixels per study, control the size (height) of Plotly figures
38
+ MAX_CHARS = 20 # Maximum number of characters for labels
39
+
40
+
41
+ def _check_extention(filename, exts):
42
+ if filename.suffix not in exts:
43
+ raise ValueError(
44
+ f'The "out_filename" provided has extension {filename.suffix}. '
45
+ f'Valid extensions are {", ".join(exts)}.'
46
+ )
47
+
48
+
49
+ def _reorder_matrix(mat, row_labels, col_labels, symmetric=False, reorder="single"):
50
+ """Reorder a matrix.
51
+
52
+ This function reorders the provided matrix. It was adaptes from
53
+ nilearn.plotting.plot_matrix._reorder_matrix to reorder non-square matrices.
54
+
55
+ License
56
+ -------
57
+ New BSD License
58
+ Copyright (c) 2007 - 2023 The nilearn developers.
59
+ Redistribution and use in source and binary forms, with or without
60
+ modification, are permitted provided that the following conditions are met:
61
+ a. Redistributions of source code must retain the above copyright notice,
62
+ this list of conditions and the following disclaimer.
63
+ b. Redistributions in binary form must reproduce the above copyright
64
+ notice, this list of conditions and the following disclaimer in the
65
+ documentation and/or other materials provided with the distribution.
66
+ c. Neither the name of the nilearn developers nor the names of
67
+ its contributors may be used to endorse or promote products
68
+ derived from this software without specific prior written
69
+ permission.
70
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
71
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
74
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
76
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
77
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
80
+ DAMAGE.
81
+ """
82
+ if not row_labels or not col_labels:
83
+ raise ValueError("Labels are needed to show the reordering.")
84
+
85
+ # Order rows
86
+ row_linkage_matrix = linkage(mat, method=reorder)
87
+ row_ordered_linkage = optimal_leaf_ordering(row_linkage_matrix, mat)
88
+ row_index = leaves_list(row_ordered_linkage)
89
+
90
+ # Make sure labels is an ndarray and copy it
91
+ row_labels = np.array(row_labels).copy()
92
+
93
+ if not symmetric:
94
+ # Order columns
95
+ col_linkage_matrix = linkage(mat.T, method=reorder)
96
+ col_ordered_linkage = optimal_leaf_ordering(col_linkage_matrix, mat.T)
97
+ col_index = leaves_list(col_ordered_linkage)
98
+
99
+ col_labels = np.array(col_labels).copy()
100
+ else:
101
+ col_index = row_index
102
+ col_labels = row_labels
103
+
104
+ mat = mat.copy()
105
+
106
+ # and reorder labels and matrix
107
+ row_labels = row_labels[row_index].tolist()
108
+ col_labels = col_labels[col_index].tolist()
109
+ mat = mat[row_index, :][:, col_index]
110
+
111
+ return mat, row_labels, col_labels
112
+
113
+
114
+ def plot_static_brain(img, out_filename, threshold=1e-06):
115
+ """Plot static brain image.
116
+
117
+ .. versionadded:: 0.1.0
118
+
119
+ Parameters
120
+ ----------
121
+ img : :obj:`~nibabel.nifti1.Nifti1Image`
122
+ Stat image to plot.
123
+ out_filename : :obj:`pathlib.Path`
124
+ The name of an image file to export the plot to.
125
+ Valid extensions are '.png', '.pdf', '.svg'.
126
+ threshold: a number, None, or 'auto', optional
127
+ If None is given, the image is not thresholded. If a number is given, it is
128
+ used to threshold the image: values below the threshold (in absolute value)
129
+ are plotted as transparent. If 'auto' is given, the threshold is determined
130
+ magically by analysis of the image. Default=1e-6.
131
+ """
132
+ _check_extention(out_filename, [".png", ".pdf", ".svg"])
133
+
134
+ template = datasets.load_mni152_template(resolution=1)
135
+ fig = plot_stat_map(
136
+ img,
137
+ bg_img=template,
138
+ black_bg=False,
139
+ draw_cross=False,
140
+ threshold=threshold,
141
+ display_mode="mosaic",
142
+ symmetric_cbar=True,
143
+ )
144
+ fig.savefig(out_filename, dpi=300)
145
+ fig.close()
146
+
147
+
148
+ def plot_mask(mask, out_filename):
149
+ """Plot mask.
150
+
151
+ .. versionadded:: 0.1.0
152
+
153
+ Parameters
154
+ ----------
155
+ img : :obj:`~nibabel.nifti1.Nifti1Image`
156
+ Mask image to plot.
157
+ out_filename : :obj:`pathlib.Path`
158
+ The name of an image file to export the plot to.
159
+ Valid extensions are '.png', '.pdf', '.svg'.
160
+ """
161
+ _check_extention(out_filename, [".png", ".pdf", ".svg"])
162
+
163
+ template = datasets.load_mni152_template(resolution=1)
164
+
165
+ fig = plot_roi(
166
+ mask,
167
+ bg_img=template,
168
+ black_bg=False,
169
+ draw_cross=False,
170
+ cmap="Blues",
171
+ vmin=0,
172
+ vmax=1,
173
+ alpha=0.7,
174
+ display_mode="mosaic",
175
+ )
176
+ fig.savefig(out_filename, dpi=300)
177
+ fig.close()
178
+
179
+
180
+ def plot_coordinates(
181
+ coordinates_df,
182
+ out_static_filename,
183
+ out_interactive_filename,
184
+ out_legend_filename,
185
+ ):
186
+ """Plot static and interactive coordinates.
187
+
188
+ .. versionadded:: 0.1.0
189
+
190
+ Parameters
191
+ ----------
192
+ coordinates_df : :obj:`pandas.DataFrame`
193
+ A DataFrame with the coordinates in the dataset.
194
+ out_static_filename : :obj:`pathlib.Path`
195
+ The name of an image file to export the static plot to.
196
+ Valid extensions are '.png', '.pdf', '.svg'.
197
+ out_interactive_filename : :obj:`pathlib.Path`
198
+ The name of an image file to export the interactive plot to.
199
+ Valid extension is '.html'.
200
+ out_legend_filename : :obj:`pathlib.Path`
201
+ The name of an image file to export the legend plot to.
202
+ Valid extensions are '.png', '.pdf', '.svg'.
203
+ """
204
+ _check_extention(out_static_filename, [".png", ".pdf", ".svg"])
205
+ _check_extention(out_interactive_filename, [".html"])
206
+ _check_extention(out_legend_filename, [".png", ".pdf", ".svg"])
207
+
208
+ node_coords = coordinates_df[["x", "y", "z"]].to_numpy()
209
+ n_coords = len(node_coords)
210
+ adjacency_matrix = np.zeros((n_coords, n_coords))
211
+
212
+ # Generate dictionary and array of colors for each unique ID
213
+ ids = coordinates_df["study_id"].to_list()
214
+ unq_ids = np.unique(ids)
215
+ cmap = plt.colormaps["tab20"].resampled(len(unq_ids))
216
+ colors_dict = {unq_id: mcolors.to_hex(cmap(i)) for i, unq_id in enumerate(unq_ids)}
217
+ colors = [colors_dict[id_] for id_ in ids]
218
+
219
+ fig = plot_connectome(adjacency_matrix, node_coords, node_color=colors)
220
+ fig.savefig(out_static_filename, dpi=300)
221
+ fig.close()
222
+
223
+ # Generate legend
224
+ patches_lst = [
225
+ mpatches.Patch(color=color, label=label) for label, color in colors_dict.items()
226
+ ]
227
+
228
+ # Plot legeng
229
+ max_len_per_page = 200
230
+ max_legend_len = max(len(id_) for id_ in unq_ids)
231
+ ncol = 1 if max_legend_len > max_len_per_page else int(max_len_per_page / max_legend_len)
232
+ labl_fig, ax = plt.subplots(1, 1)
233
+ labl_fig.legend(
234
+ handles=patches_lst,
235
+ ncol=ncol,
236
+ fontsize=10,
237
+ loc="center",
238
+ )
239
+ ax.axis("off")
240
+ labl_fig.savefig(out_legend_filename, bbox_inches="tight", dpi=300)
241
+ plt.close()
242
+
243
+ # Plot interactive connectome
244
+ html_view = view_connectome(
245
+ adjacency_matrix,
246
+ node_coords,
247
+ node_size=10,
248
+ colorbar=False,
249
+ node_color=colors,
250
+ )
251
+ html_view.save_as_html(out_interactive_filename)
252
+
253
+
254
+ def plot_interactive_brain(img, out_filename, threshold=1e-06):
255
+ """Plot interactive brain image.
256
+
257
+ .. versionadded:: 0.1.0
258
+
259
+ Parameters
260
+ ----------
261
+ img : :obj:`~nibabel.nifti1.Nifti1Image`
262
+ Stat image to plot.
263
+ out_filename : :obj:`pathlib.Path`
264
+ The name of an image file to export the plot to. Valid extension is '.html'.
265
+ threshold: a number, None, or 'auto', optional
266
+ If None is given, the image is not thresholded. If a number is given, it is
267
+ used to threshold the image: values below the threshold (in absolute value)
268
+ are plotted as transparent. If 'auto' is given, the threshold is determined
269
+ magically by analysis of the image. Default=1e-6.
270
+ """
271
+ _check_extention(out_filename, [".html"])
272
+
273
+ template = datasets.load_mni152_template(resolution=1)
274
+ html_view = view_img(
275
+ img,
276
+ bg_img=template,
277
+ black_bg=False,
278
+ threshold=threshold,
279
+ symmetric_cmap=True,
280
+ )
281
+ html_view.save_as_html(out_filename)
282
+
283
+
284
+ def plot_heatmap(
285
+ data_df,
286
+ out_filename,
287
+ symmetric=False,
288
+ reorder="single",
289
+ cmap="Reds",
290
+ zmin=None,
291
+ zmax=None,
292
+ ):
293
+ """Plot heatmap.
294
+
295
+ .. versionadded:: 0.1.0
296
+
297
+ Parameters
298
+ ----------
299
+ data_df : :obj:`pandas.DataFrame`
300
+ A DataFrame with the data for the heatmap. It could be a correlation matrix or
301
+ a contribution matrix with information about the relative contributions of
302
+ each experiment to each cluster in the thresholded map.
303
+ out_filename : :obj:`pathlib.Path`
304
+ The name of an image file to export the plot to.
305
+ Valid extension is '.html'.
306
+ symmetric : :obj:`bool`, optional
307
+ Whether to reorder the matrix symmetrically. Use True if using a correlation matrix.
308
+ Default is False.
309
+ reorder : :obj:`str`, optional
310
+ The method to use for reordering the matrix. Default is 'average'.
311
+ cmap : :obj:`str`, optional
312
+ The colormap to use. Default is 'Reds'.
313
+ zmin : :obj:`float`, optional
314
+ The minimum value to use for the colormap. Default is None.
315
+ zmax : :obj:`float`, optional
316
+ The maximum value to use for the colormap. Default is None.
317
+ """
318
+ _check_extention(out_filename, [".html"])
319
+
320
+ n_studies, n_clusters = data_df.shape
321
+ if (n_studies > 2) and (n_clusters > 2):
322
+ # Reorder matrix only if more than 1 cluster/experiment
323
+ mat = data_df.to_numpy()
324
+ row_labels, col_labels = (
325
+ data_df.index.to_list(),
326
+ data_df.columns.to_list(),
327
+ )
328
+ new_mat, new_row_labels, new_col_labels = _reorder_matrix(
329
+ mat,
330
+ row_labels,
331
+ col_labels,
332
+ symmetric=symmetric,
333
+ reorder=reorder,
334
+ )
335
+
336
+ # Truncate labels to MAX_CHARS characters
337
+ x_labels = [label[:MAX_CHARS] for label in new_col_labels]
338
+ y_labels = [label[:MAX_CHARS] for label in new_row_labels]
339
+ data_df = pd.DataFrame(new_mat, columns=x_labels, index=y_labels)
340
+
341
+ fig = px.imshow(data_df, color_continuous_scale=cmap, zmin=zmin, zmax=zmax, aspect="equal")
342
+
343
+ height = n_studies * PXS_PER_STD
344
+ fig.update_layout(autosize=True, height=height)
345
+ fig.write_html(out_filename, full_html=True, include_plotlyjs=True)
346
+
347
+
348
+ def gen_table(clusters_table, out_filename):
349
+ """Generate table.
350
+
351
+ .. versionadded:: 0.1.0
352
+
353
+ Parameters
354
+ ----------
355
+ clusters_table : :obj:`pandas.DataFrame`
356
+ A DataFrame with information about each cluster.
357
+ out_filename : :obj:`pathlib.Path`
358
+ The name of an image file to export the plot to.
359
+ Valid extension is '.html'.
360
+ """
361
+ _check_extention(out_filename, [".html"])
362
+
363
+ clust_ids = clusters_table["Cluster ID"].to_list()
364
+ clusters_table = clusters_table.drop(columns=["Cluster ID"])
365
+
366
+ tail = [c_id.split(" ")[0].split("Tail")[0] for c_id in clust_ids]
367
+ ids = [c_id.split(" ")[1] for c_id in clust_ids]
368
+ tuples = list(zip(*[tail, ids]))
369
+ row = pd.MultiIndex.from_tuples(tuples)
370
+ clusters_table.index = row
371
+ clusters_table.index = clusters_table.index.rename(["Tail", "Cluster ID"])
372
+
373
+ styled_df = clusters_table.style.format(precision=2).set_table_styles(TABLE_STYLE)
374
+ styled_df.to_html(out_filename)
375
+
376
+
377
+ def plot_clusters(img, out_filename):
378
+ """Plot clusters.
379
+
380
+ .. versionadded:: 0.1.0
381
+
382
+ Parameters
383
+ ----------
384
+ img : :obj:`~nibabel.nifti1.Nifti1Image`
385
+ Label image to plot.
386
+ out_filename : :obj:`pathlib.Path`
387
+ The name of an image file to export the plot to.
388
+ Valid extensions are '.png', '.pdf', '.svg'.
389
+ """
390
+ _check_extention(out_filename, [".png", ".pdf", ".svg"])
391
+
392
+ template = datasets.load_mni152_template(resolution=1)
393
+
394
+ # Define cmap depending on the number of clusters
395
+ clust_ids = list(np.unique(img.get_fdata())[1:])
396
+ cmap = plt.colormaps["tab20"].resampled(len(clust_ids))
397
+
398
+ fig = plot_roi(
399
+ img,
400
+ bg_img=template,
401
+ black_bg=False,
402
+ draw_cross=False,
403
+ cmap=cmap,
404
+ alpha=0.8,
405
+ colorbar=True,
406
+ display_mode="mosaic",
407
+ )
408
+ fig.savefig(out_filename, dpi=300)
409
+ fig.close()
410
+
411
+
412
+ def _plot_true_voxels(maps_arr, ids_, out_filename):
413
+ """Plot percentage of valid voxels.
414
+
415
+ .. versionadded:: 0.2.2
416
+
417
+ """
418
+ n_studies, n_voxels = maps_arr.shape
419
+ mask = ~np.isnan(maps_arr) & (maps_arr != 0)
420
+
421
+ x_label, y_label = "Voxels Included", "ID"
422
+ perc_voxs = mask.sum(axis=1) / n_voxels
423
+ valid_df = pd.DataFrame({y_label: ids_, x_label: perc_voxs})
424
+ valid_sorted_df = valid_df.sort_values(x_label, ascending=True)
425
+
426
+ fig = px.bar(
427
+ valid_sorted_df,
428
+ x=x_label,
429
+ y=y_label,
430
+ orientation="h",
431
+ color=x_label,
432
+ color_continuous_scale="blues",
433
+ range_color=(0, 1),
434
+ )
435
+
436
+ fig.update_xaxes(
437
+ showline=True,
438
+ linewidth=2,
439
+ linecolor="black",
440
+ visible=True,
441
+ showticklabels=False,
442
+ title=None,
443
+ )
444
+ fig.update_yaxes(
445
+ showline=True,
446
+ linewidth=2,
447
+ linecolor="black",
448
+ visible=True,
449
+ ticktext=valid_sorted_df[y_label].str.slice(0, MAX_CHARS).tolist(),
450
+ )
451
+
452
+ height = n_studies * PXS_PER_STD
453
+ fig.update_layout(
454
+ height=height,
455
+ autosize=True,
456
+ font_size=14,
457
+ plot_bgcolor="white",
458
+ xaxis_gridcolor="white",
459
+ yaxis_gridcolor="white",
460
+ xaxis_gridwidth=2,
461
+ showlegend=False,
462
+ )
463
+ fig.write_html(out_filename, full_html=True, include_plotlyjs=True)
464
+
465
+
466
+ def _plot_ridgeplot(maps_arr, ids_, x_label, out_filename):
467
+ """Plot histograms of the images.
468
+
469
+ .. versionadded:: 0.2.0
470
+
471
+ """
472
+ n_studies = len(ids_)
473
+ labels = [id_[:MAX_CHARS] for id_ in ids_] # Truncate labels to MAX_CHARS characters
474
+
475
+ mask = ~np.isnan(maps_arr) & (maps_arr != 0)
476
+ maps_lst = [maps_arr[i][mask[i]] for i in range(n_studies)]
477
+
478
+ N_KDE_POINTS = 100
479
+ max_val = 8 if x_label == "Z" else 1
480
+ kde_points = np.linspace(-max_val, max_val, N_KDE_POINTS)
481
+ bandwidth = 0.5 if x_label == "Z" else 0.1
482
+
483
+ fig = ridgeplot(
484
+ samples=maps_lst,
485
+ labels=labels,
486
+ coloralpha=0.98,
487
+ bandwidth=bandwidth,
488
+ kde_points=kde_points,
489
+ colorscale="Bluered",
490
+ colormode="mean-means",
491
+ spacing=PXS_PER_STD / 100,
492
+ linewidth=2,
493
+ )
494
+
495
+ height = n_studies * PXS_PER_STD
496
+ fig.update_layout(
497
+ height=height,
498
+ autosize=True,
499
+ font_size=14,
500
+ plot_bgcolor="white",
501
+ xaxis_gridcolor="white",
502
+ yaxis_gridcolor="white",
503
+ xaxis_gridwidth=2,
504
+ xaxis_title=x_label,
505
+ showlegend=False,
506
+ )
507
+ fig.write_html(out_filename, full_html=True, include_plotlyjs=True)
508
+
509
+
510
+ def _plot_sumstats(maps_arr, ids_, out_filename):
511
+ """Plot summary statistics of the images.
512
+
513
+ .. versionadded:: 0.2.2
514
+
515
+ """
516
+ n_studies = len(ids_)
517
+ mask = ~np.isnan(maps_arr) & (maps_arr != 0)
518
+ maps_lst = [maps_arr[i][mask[i]] for i in range(n_studies)]
519
+
520
+ stats_lbls = [
521
+ "Mean",
522
+ "STD",
523
+ "Var",
524
+ "Median",
525
+ "Mode",
526
+ "Min",
527
+ "Max",
528
+ "Skew",
529
+ "Kurtosis",
530
+ "Range",
531
+ "Moment",
532
+ "IQR",
533
+ ]
534
+ scores, id_lst = [], []
535
+ for id_, map_ in zip(ids_, maps_lst):
536
+ scores.extend(
537
+ [
538
+ np.mean(map_),
539
+ np.std(map_),
540
+ np.var(map_),
541
+ np.median(map_),
542
+ stats.mode(map_)[0],
543
+ np.min(map_),
544
+ np.max(map_),
545
+ stats.skew(map_),
546
+ stats.kurtosis(map_),
547
+ np.max(map_) - np.min(map_),
548
+ stats.moment(map_, moment=4),
549
+ stats.iqr(map_),
550
+ ]
551
+ )
552
+ id_lst.extend([id_] * len(stats_lbls))
553
+
554
+ stats_labels = stats_lbls * n_studies
555
+ data_df = pd.DataFrame({"ID": id_lst, "Score": scores, "Stat": stats_labels})
556
+
557
+ fig = px.strip(
558
+ data_df,
559
+ y="Score",
560
+ color="ID",
561
+ facet_col="Stat",
562
+ stripmode="group",
563
+ facet_col_wrap=4,
564
+ facet_col_spacing=0.08,
565
+ )
566
+
567
+ fig.update_xaxes(showline=True, linewidth=2, linecolor="black", mirror=True)
568
+ fig.update_yaxes(
569
+ constrain="domain",
570
+ matches=None,
571
+ showline=True,
572
+ linewidth=2,
573
+ linecolor="black",
574
+ mirror=True,
575
+ title=None,
576
+ )
577
+ fig.update_layout(
578
+ height=900,
579
+ autosize=True,
580
+ font_size=14,
581
+ plot_bgcolor="white",
582
+ xaxis_gridcolor="white",
583
+ yaxis_gridcolor="white",
584
+ xaxis_gridwidth=2,
585
+ showlegend=False,
586
+ )
587
+ fig.for_each_yaxis(lambda yaxis: yaxis.update(showticklabels=True))
588
+ fig.write_html(out_filename, full_html=True, include_plotlyjs=True)
589
+
590
+
591
+ def _plot_relcov_map(maps_arr, masker, out_filename):
592
+ """Plot relative coverage map.
593
+
594
+ .. versionadded:: 0.2.0
595
+
596
+ """
597
+ _check_extention(out_filename, [".png", ".pdf", ".svg"])
598
+
599
+ epsilon = 1e-05
600
+
601
+ # Binaries maps and create relative coverage map
602
+ binary_maps_arr = np.where((-epsilon > maps_arr) | (maps_arr > epsilon), 1, 0)
603
+ coverage_arr = np.sum(binary_maps_arr, axis=0) / binary_maps_arr.shape[0]
604
+
605
+ coverage_img = masker.inverse_transform(coverage_arr)
606
+
607
+ # Plot coverage map
608
+ template = datasets.load_mni152_template(resolution=1)
609
+ fig = plot_img(
610
+ coverage_img,
611
+ bg_img=template,
612
+ black_bg=False,
613
+ draw_cross=False,
614
+ threshold=epsilon,
615
+ alpha=0.7,
616
+ colorbar=True,
617
+ cmap="Blues",
618
+ vmin=0,
619
+ vmax=1,
620
+ display_mode="mosaic",
621
+ )
622
+ fig.savefig(out_filename, dpi=300)
623
+ fig.close()
624
+
625
+
626
+ def _plot_dof_map(dof_map, out_filename):
627
+ """Plot DoF map.
628
+
629
+ .. versionadded:: 0.2.1
630
+
631
+ """
632
+ _check_extention(out_filename, [".png", ".pdf", ".svg"])
633
+
634
+ epsilon = 1e-05
635
+
636
+ # Plot coverage map
637
+ template = datasets.load_mni152_template(resolution=1)
638
+ fig = plot_img(
639
+ dof_map,
640
+ bg_img=template,
641
+ black_bg=False,
642
+ draw_cross=False,
643
+ threshold=epsilon,
644
+ alpha=0.7,
645
+ colorbar=True,
646
+ cmap="YlOrRd",
647
+ vmin=0,
648
+ display_mode="mosaic",
649
+ )
650
+ fig.savefig(out_filename, dpi=300)
651
+ fig.close()