vima-spatial 0.2.5__tar.gz → 0.2.7__tar.gz

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 (40) hide show
  1. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/PKG-INFO +1 -1
  2. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/setup.cfg +1 -1
  3. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/_settings.py +154 -0
  4. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/dimreduce.py +2 -2
  5. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/ingest.py +6 -5
  6. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/st.py +5 -3
  7. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/train/logging.py +4 -1
  8. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/features.py +15 -2
  9. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/patches.py +3 -3
  10. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/patchexamples.py +12 -11
  11. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/spatial.py +25 -7
  12. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/umaps.py +8 -1
  13. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima_spatial.egg-info/PKG-INFO +1 -1
  14. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/README.md +0 -0
  15. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/pyproject.toml +0 -0
  16. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/__init__.py +0 -0
  17. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/cc.py +0 -0
  18. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/data/__init__.py +0 -0
  19. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/data/download.py +0 -0
  20. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/data/patchcollection.py +0 -0
  21. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/data/samples.py +0 -0
  22. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/fingerprints.py +0 -0
  23. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/__init__.py +0 -0
  24. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/nonst.py +0 -0
  25. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/ingest/util.py +0 -0
  26. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/__init__.py +0 -0
  27. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/resnet_vae.py +0 -0
  28. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/resnetlight_decoder.py +0 -0
  29. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/resnetlight_encoder.py +0 -0
  30. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/simple_vae.py +0 -0
  31. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/models/vae.py +0 -0
  32. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/patchfeatures.py +0 -0
  33. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/train/__init__.py +0 -0
  34. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/train/training.py +0 -0
  35. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima/vis/__init__.py +0 -0
  36. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima_spatial.egg-info/SOURCES.txt +0 -0
  37. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima_spatial.egg-info/dependency_links.txt +0 -0
  38. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima_spatial.egg-info/requires.txt +0 -0
  39. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/src/vima_spatial.egg-info/top_level.txt +0 -0
  40. {vima_spatial-0.2.5 → vima_spatial-0.2.7}/tests/test_ra_regression.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vima-spatial
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: variational inference-based microniche analysis
5
5
  Home-page: https://github.com/yakirr/vima
6
6
  Author: Yakir Reshef
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = vima-spatial
3
- version = 0.2.5
3
+ version = 0.2.7
4
4
  author = Yakir Reshef
5
5
  author_email = yreshef@broadinstitute.org
6
6
  description = variational inference-based microniche analysis
@@ -30,6 +30,19 @@ Three independent knobs:
30
30
  to verbosity (bars stay on even at ``"minimal"``); set ``False`` for batch or
31
31
  cluster runs.
32
32
 
33
+ Plots that *are* drawn go through ``settings.show()``, which displays them when a
34
+ display is available and otherwise saves them as PNGs. This matters on a cluster:
35
+ with no display matplotlib falls back to the Agg backend, where ``plt.show()``
36
+ silently discards the figure and leaves it open, so the plots are lost and the
37
+ figures accumulate. Two knobs control the saving:
38
+
39
+ * ``settings.save_plots`` -- ``"auto"`` (default) saves only when no display is
40
+ available, so notebooks are unaffected; ``True`` always saves (never displays),
41
+ ``False`` never saves.
42
+ * ``settings.plot_dir`` -- directory for saved plots, created on demand; defaults
43
+ to ``"figs"``. Set to ``None`` to discard plots instead (a warning is issued
44
+ once).
45
+
33
46
  Guidelines for package code:
34
47
 
35
48
  * ``logger.info(...)`` -- normal progress messages (visible at ``default``).
@@ -39,8 +52,12 @@ Guidelines for package code:
39
52
  * ``settings.progress(iterable, name=...)`` -- wrap any loop needing a bar.
40
53
  * ``if settings.show_plots(...):`` -- guard a diagnostic plot. Pass ``"verbose"``
41
54
  for the detailed plots; the default level guards the standard ones.
55
+ * ``settings.show(name)`` -- finish a figure. Never call ``plt.show()`` directly:
56
+ that displays or discards, with no way to save.
42
57
  """
43
58
 
59
+ import os
60
+ import re
44
61
  import sys
45
62
  import logging
46
63
  from enum import IntEnum
@@ -81,6 +98,32 @@ _LOGGING_LEVELS = {
81
98
 
82
99
  logger = logging.getLogger("vima")
83
100
 
101
+ # matplotlib backends that render to a file and cannot display anything; a
102
+ # terminal with no display falls back to "agg". Names are matched lowercase.
103
+ # Fallback for the query below, and what matplotlib has reported for years.
104
+ _FILE_ONLY_BACKENDS = {"agg", "cairo", "pdf", "pgf", "ps", "svg", "template"}
105
+
106
+
107
+ def _file_only_backends():
108
+ """Names of the backends that can only write to a file.
109
+
110
+ Asked of matplotlib where it exposes the list (3.9+), so a backend added
111
+ later is picked up, with :data:`_FILE_ONLY_BACKENDS` as the fallback.
112
+
113
+ Deliberately phrased as "which backends are file-only" rather than "which
114
+ are interactive", even though matplotlib offers both: it classifies
115
+ Jupyter's inline backend as *non*-interactive, since it drives no GUI event
116
+ loop -- yet inline plots do appear in front of the user, which is the only
117
+ thing we need to know. ``resolve_backend`` would answer that question, but
118
+ it imports the backend module and raises on unknown names, so we avoid it.
119
+ """
120
+ try:
121
+ from matplotlib.backends.registry import backend_registry, BackendFilter
122
+
123
+ return set(backend_registry.list_builtin(BackendFilter.NON_INTERACTIVE))
124
+ except Exception:
125
+ return _FILE_ONLY_BACKENDS
126
+
84
127
  # ANSI colors: debug messages are grayed out, results are green.
85
128
  _GRAY, _GREEN, _RESET = "\033[90m", "\033[32m", "\033[0m"
86
129
 
@@ -135,6 +178,12 @@ class Settings:
135
178
  logger.propagate = False
136
179
 
137
180
  self.progress_bars = True
181
+ self.plot_dir = "figs"
182
+ self.save_plots = "auto"
183
+ self._plot_count = 0
184
+ self._last_figure = None
185
+ self._warned_plots_discarded = False
186
+ self._warned_save_failed = False
138
187
  self._verbosity = None
139
188
  # ``diagnostic_plots`` tracks ``verbosity`` until the user sets it.
140
189
  self._diagnostic_plots = None
@@ -174,6 +223,111 @@ class Settings:
174
223
  """
175
224
  return self._diagnostic_plots >= Verbosity.parse(level)
176
225
 
226
+ def _display_available(self):
227
+ """Whether the active matplotlib backend can actually show a figure.
228
+
229
+ A backend is a display unless it is one of the file-only ones (see
230
+ :func:`_file_only_backends`). Deciding it that way around means
231
+ anything unrecognized -- a third-party or future backend, or a
232
+ ``module://`` backend from an embedding host such as Jupyter's
233
+ ``matplotlib_inline`` or ``ipympl`` -- counts as a display, so the
234
+ worst case is plain ``plt.show()``, exactly as before this setting
235
+ existed.
236
+ """
237
+ import matplotlib
238
+
239
+ try:
240
+ backend = matplotlib.get_backend()
241
+ except Exception: # backend resolution can fail in odd environments
242
+ return True
243
+ return backend.lower() not in _file_only_backends()
244
+
245
+ def _saving_plots(self):
246
+ """Whether :meth:`show` should save rather than display."""
247
+ if self.save_plots == "auto":
248
+ return not self._display_available()
249
+ return bool(self.save_plots)
250
+
251
+ def show(self, name=None, fig=None, overwrite=False):
252
+ """Display the current figure, or save it when there is no display.
253
+
254
+ Central replacement for ``plt.show()`` in package code. With a display
255
+ available this is exactly ``plt.show()``. Without one -- a plain
256
+ terminal on a cluster, where ``plt.show()`` silently discards the
257
+ figure *and* leaves it open, so figures pile up -- the figure is
258
+ written to ``settings.plot_dir`` as a PNG and closed.
259
+
260
+ ``name`` labels the file, defaulting to the name of the calling
261
+ function. Files are numbered in the order they are produced, so a run's
262
+ plots sort chronologically; pass ``overwrite=True`` to use ``name``
263
+ alone as the filename and replace the file on every call (for a plot
264
+ redrawn repeatedly, like the per-epoch training summary). ``fig`` is the
265
+ figure to save, defaulting to the current one -- pass it explicitly when
266
+ drawing onto a figure that an earlier :meth:`show` may already have
267
+ closed, since ``plt.gcf()`` would then hand back a blank one.
268
+
269
+ Returns the path written, or ``None`` if the figure was displayed or
270
+ discarded.
271
+ """
272
+ import matplotlib.pyplot as plt
273
+
274
+ if fig is None:
275
+ fig = plt.gcf()
276
+ self._last_figure = fig
277
+
278
+ if not self._saving_plots():
279
+ plt.show()
280
+ return None
281
+
282
+ if self.plot_dir is None:
283
+ if not self._warned_plots_discarded:
284
+ logger.warning(
285
+ "no display available and vima.settings.plot_dir is None, so "
286
+ "plots are being discarded; set vima.settings.plot_dir to save "
287
+ "them as images instead"
288
+ )
289
+ self._warned_plots_discarded = True
290
+ plt.close(fig)
291
+ return None
292
+
293
+ if name is None:
294
+ name = sys._getframe(1).f_code.co_name
295
+ name = re.sub(r"[^0-9a-zA-Z]+", "_", str(name)).strip("_").lower() or "plot"
296
+ if overwrite:
297
+ filename = f"{name}.png"
298
+ else:
299
+ self._plot_count += 1
300
+ filename = f"{self._plot_count:03d}_{name}.png"
301
+
302
+ path = os.path.join(self.plot_dir, filename)
303
+ try:
304
+ os.makedirs(self.plot_dir, exist_ok=True)
305
+ fig.savefig(path, dpi=150, bbox_inches="tight")
306
+ except OSError as e:
307
+ # A diagnostic plot is never worth aborting a long run for.
308
+ if not self._warned_save_failed:
309
+ logger.warning(f"could not save plot to {path}: {e}")
310
+ self._warned_save_failed = True
311
+ plt.close(fig)
312
+ return None
313
+ plt.close(fig)
314
+ logger.info(f"saved plot to {path}")
315
+ return path
316
+
317
+ def current_figure(self):
318
+ """The figure a follow-up call should draw on.
319
+
320
+ Normally ``plt.gcf()``. When plots are being saved instead of shown,
321
+ :meth:`show` closes each figure, so ``plt.gcf()`` would hand back a
322
+ fresh blank one; with no figure open, return the last figure
323
+ :meth:`show` handled instead.
324
+ """
325
+ import matplotlib.pyplot as plt
326
+
327
+ if not plt.get_fignums() and self._last_figure is not None:
328
+ return self._last_figure
329
+ return plt.gcf()
330
+
177
331
  def progress(self, iterable=None, name=None, total=None, ncols=100, desc=None, **kwargs):
178
332
  """tqdm wrapper honoring ``settings.progress_bars``.
179
333
 
@@ -91,7 +91,7 @@ def metapixels_allsamples(normedpixelsdir, masksdir, sids, total_n_metapixels):
91
91
  plt.xlabel('# non-empty pixels per metapixel')
92
92
  plt.ylabel('Frequency')
93
93
  plt.tight_layout()
94
- plt.show()
94
+ settings.show('metapixel_occupancy_cdf')
95
95
 
96
96
  return all_metapixels, all_npixels
97
97
 
@@ -174,7 +174,7 @@ def pca_metapixels(mps, k):
174
174
  plt.imshow(loadings, cmap='seismic', vmin=-0.5, vmax=0.5)
175
175
  plt.yticks(range(len(loadings)), loadings.index)
176
176
  plt.xticks(range(len(loadings.columns)), loadings.columns, rotation=90)
177
- plt.show()
177
+ settings.show('pc_loadings')
178
178
 
179
179
  return loadings, C, allmp
180
180
 
@@ -65,9 +65,10 @@ def visualize_pixels(pixels, ntoplot, input, colorby):
65
65
  if settings.show_plots('verbose'):
66
66
  sns.scatterplot(x='PC1', y='PC2', hue=metavar, data=toplot, palette='Set1', s=1, legend=False)
67
67
  plt.title(metavar)
68
- plt.show()
68
+ settings.show(f'pc1_pc2_by_{metavar}')
69
69
  sc.pl.umap(toplot_ad, color=metavar, legend_loc=None, frameon=False,
70
- title=f'pixels UMAPed using {input}, colored by {metavar}')
70
+ title=f'pixels UMAPed using {input}, colored by {metavar}', show=False)
71
+ settings.show(f'pixel_umap_by_{metavar}')
71
72
 
72
73
  # print LISI ratio
73
74
  n_unique = toplot_ad.obs[metavar].nunique()
@@ -250,7 +251,7 @@ def sanity_checks(outdir, repname, npcs=1, nskip=3):
250
251
  s = da.astype(np.float32)
251
252
  da.close(); del da
252
253
  s.plot(col='marker', col_wrap=5, vmin=-10, vmax=10, cmap='seismic')
253
- plt.show()
254
+ settings.show(f'all_pcs_{sids[0]}')
254
255
  del s
255
256
 
256
257
  logger.info('histogram of each pc')
@@ -270,7 +271,7 @@ def sanity_checks(outdir, repname, npcs=1, nskip=3):
270
271
  plt.subplot(int(np.ceil(nmms/4)), 4, i+1)
271
272
  plt.hist(harmpixels[:,i], bins=1000)
272
273
  plt.tight_layout()
273
- plt.show()
274
+ settings.show('pc_histograms')
274
275
  del harmpixels
275
276
  gc.collect()
276
277
 
@@ -290,4 +291,4 @@ def sanity_checks(outdir, repname, npcs=1, nskip=3):
290
291
  ax.set_title(sid)
291
292
  del s; gc.collect()
292
293
  plt.tight_layout()
293
- plt.show()
294
+ settings.show(f'pc{i}_by_sample')
@@ -115,7 +115,8 @@ def get_sumstats(load, filepaths, target_sum, x_col, y_col, gene_col, n_top_gene
115
115
  hvgs = hvgs + list(set(genes_to_add) & set(pl.var_names))
116
116
 
117
117
  if settings.show_plots():
118
- sc.pl.highly_variable_genes(pl, log=True, show=True)
118
+ sc.pl.highly_variable_genes(pl, log=True, show=False)
119
+ settings.show(f'hvgs_{sid}')
119
120
 
120
121
  if settings.show_plots('verbose'):
121
122
  top8 = (
@@ -144,7 +145,7 @@ def get_sumstats(load, filepaths, target_sum, x_col, y_col, gene_col, n_top_gene
144
145
  ax.set_aspect('equal')
145
146
  ax.axis('off')
146
147
  plt.tight_layout()
147
- plt.show()
148
+ settings.show(f'top_hvgs_{sid}')
148
149
  plt.close(fig)
149
150
 
150
151
  union_hvgs.update(hvgs)
@@ -220,7 +221,8 @@ def transcriptlist_to_normedpixelmatrix(sid, data, x_col, y_col, gene_col, pixel
220
221
  pl = pl[(pl[markers].sum(axis=1) >= min_ntranscripts_per_pixel) ]#& (pl[list(set(markers) & set(genes))].sum(axis=1) > 0)]
221
222
  if settings.show_plots():
222
223
  plt.scatter(pl.pixel_x, pl.pixel_y, c=pl[markers].sum(axis=1), s=0.1, alpha=0.8, vmin=0, vmax=100)
223
- plt.gca().set_aspect('equal'); plt.title('transcript density (gray = failed qc)'); plt.axis('off'); plt.show()
224
+ plt.gca().set_aspect('equal'); plt.title('transcript density (gray = failed qc)'); plt.axis('off')
225
+ settings.show(f'transcript_density_{sid}')
224
226
  logger.info(f'\t{len(pl)} pixels after QC.')
225
227
 
226
228
  logger.info('\tLog-normalizing...')
@@ -4,6 +4,7 @@ import time
4
4
  import matplotlib.pyplot as plt
5
5
  from IPython import display
6
6
  from .. import vis as v
7
+ from .._settings import settings
7
8
 
8
9
  class LossLogger:
9
10
  """Accumulate and report per-model training/validation losses; returned by `train`."""
@@ -118,7 +119,9 @@ class LossLogger:
118
119
  plt.title('Reconstruction error across validation patches')
119
120
  plt.xlabel('Reconstruction error'); plt.ylabel('#Patches')
120
121
  plt.gca().spines[['top', 'right']].set_visible(False)
121
- plt.show()
122
+ # Redrawn every epoch, so keep overwriting one file rather than
123
+ # accumulating one per epoch.
124
+ settings.show('training_progress', overwrite=True)
122
125
 
123
126
  if self.detailed:
124
127
  ix = np.argsort(vrlosses)
@@ -2,6 +2,7 @@ import matplotlib.pyplot as plt
2
2
  import numpy as np
3
3
  import pandas as pd
4
4
  import seaborn as sns
5
+ from .._settings import settings
5
6
 
6
7
 
7
8
  def _select_features(features, group_a, group_b, n_top, n_bottom, markers):
@@ -49,6 +50,11 @@ def plot_features(
49
50
  ``[label_a, label_b]`` for the legend.
50
51
  kind
51
52
  'violin' (default), 'box', or 'swarm'.
53
+
54
+ Returns
55
+ -------
56
+ matplotlib.figure.Figure
57
+ The figure that was drawn.
52
58
  """
53
59
  if ax is None:
54
60
  ax = plt.gca()
@@ -70,7 +76,8 @@ def plot_features(
70
76
  plot_kwargs.update(kwargs)
71
77
  plot_fn(data=df, x='marker', y='value', hue='status', order=toplot, ax=ax, **plot_kwargs)
72
78
  if show:
73
- plt.show()
79
+ settings.show()
80
+ return ax.figure
74
81
 
75
82
 
76
83
  def plot_features_by_sample(
@@ -114,6 +121,11 @@ def plot_features_by_sample(
114
121
  ``[label_a, label_b]`` for the legend.
115
122
  connect
116
123
  Draw lines linking each sample's two group means.
124
+
125
+ Returns
126
+ -------
127
+ matplotlib.figure.Figure
128
+ The figure that was drawn.
117
129
  """
118
130
  if ax is None:
119
131
  ax = plt.gca()
@@ -147,4 +159,5 @@ def plot_features_by_sample(
147
159
  color='gray', alpha=0.4, lw=0.8, zorder=0)
148
160
 
149
161
  if show:
150
- plt.show()
162
+ settings.show()
163
+ return ax.figure
@@ -30,7 +30,7 @@ def _plot_separate(patches, markers, vmin, vmax, cmap='seismic', show=True):
30
30
  axes[k, 0].set_ylabel(marker, fontsize=9)
31
31
  plt.tight_layout()
32
32
  if show:
33
- plt.show()
33
+ settings.show()
34
34
  return fig
35
35
 
36
36
 
@@ -94,7 +94,7 @@ def _plot_composite(patches, markers, colors, vmin, vmax, features=None, nx=5, n
94
94
  if subfig is None:
95
95
  plt.tight_layout(rect=[0, 0.08, 1, 1])
96
96
  if show:
97
- plt.show()
97
+ settings.show()
98
98
  return fig
99
99
 
100
100
 
@@ -441,5 +441,5 @@ def show_patches_cells(patchmeta, cells, x_col, y_col, celltype_col,
441
441
  plt.tight_layout(rect=[0, bottom_margin, 1, 1])
442
442
 
443
443
  if show:
444
- plt.show()
444
+ settings.show()
445
445
  return fig
@@ -4,6 +4,7 @@ import torch
4
4
  import scanpy as sc
5
5
  from scipy.optimize import linear_sum_assignment
6
6
  from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
7
+ from .._settings import settings
7
8
 
8
9
 
9
10
  def scaler(minimum=0, maximum=255):
@@ -52,7 +53,8 @@ def plot_with_reconstruction(model, examples, show=True, channels=[0,1,2], pmin=
52
53
 
53
54
  if show:
54
55
  plt.tight_layout()
55
- plt.show()
56
+ settings.show()
57
+ return fig
56
58
 
57
59
 
58
60
  def plot_patches_separatechannels(examples, choose=None, vmax=10, vmin=None, channels=[0,1,2], channelnames=None):
@@ -74,7 +76,8 @@ def plot_patches_separatechannels(examples, choose=None, vmax=10, vmin=None, cha
74
76
  plt.gca().text(-5, 20, channelnames[j], va='center', ha='right', rotation=90)
75
77
 
76
78
  plt.tight_layout()
77
- plt.show()
79
+ settings.show()
80
+ return fig
78
81
 
79
82
 
80
83
  # colormaps consists of tuples of the form [channel, color, scaler]
@@ -83,8 +86,6 @@ def plot_patches_overlaychannels(examples, colormaps, nx=5, ny=5, show=True, see
83
86
  if seed is not None: np.random.seed(seed)
84
87
  ix = np.random.choice(range(len(examples)), size=nx*ny, replace=False)
85
88
  examples = examples[ix]
86
- else:
87
- ix = range(len(examples))
88
89
 
89
90
  images = apply_colormap(examples, colormaps)
90
91
 
@@ -95,8 +96,8 @@ def plot_patches_overlaychannels(examples, colormaps, nx=5, ny=5, show=True, see
95
96
  plt.axis('off')
96
97
  plt.tight_layout()
97
98
  if show:
98
- plt.show()
99
- return ix
99
+ settings.show()
100
+ return fig
100
101
 
101
102
 
102
103
  def plot_patches_overlaychannels_linsum(patches, latents, colormaps, nx=5, ny=5, show=True, seed=None,
@@ -144,9 +145,8 @@ def plot_patches_overlaychannels_linsum(patches, latents, colormaps, nx=5, ny=5,
144
145
  axs[-1,-1].add_artist(scalebar)
145
146
 
146
147
  if show:
147
- plt.show()
148
- else:
149
- return fig
148
+ settings.show()
149
+ return fig
150
150
 
151
151
 
152
152
  def plot_patches_overlaychannels_sorted(examples, colormaps, labels=None, nx=5, ny=5, show=True):
@@ -161,7 +161,8 @@ def plot_patches_overlaychannels_sorted(examples, colormaps, labels=None, nx=5,
161
161
  plt.axis('off')
162
162
  plt.tight_layout()
163
163
  if show:
164
- plt.show()
164
+ settings.show()
165
+ return fig
165
166
 
166
167
 
167
168
  # each color channel should be a tuple of the form (channel, scaler)
@@ -178,4 +179,4 @@ def plot_patches_fourcolors(examples, nx=5, ny=5,
178
179
  if yellow[0] is not None:
179
180
  colormaps.append([yellow[0], [1,1,0], yellow[1]])
180
181
 
181
- plot_patches_overlaychannels(examples, colormaps, nx=nx, ny=ny, show=show)
182
+ return plot_patches_overlaychannels(examples, colormaps, nx=nx, ny=ny, show=show)
@@ -20,6 +20,11 @@ def plot_sample_with_patches(s, marker, patchmeta, remove_margin=False, ax=None,
20
20
  Patch metadata; patches belonging to this sample are outlined.
21
21
  remove_margin
22
22
  Crop to a margin around the outlined patches.
23
+
24
+ Returns
25
+ -------
26
+ matplotlib.figure.Figure
27
+ The figure that was drawn.
23
28
  """
24
29
  if ax is None: ax = plt.gca()
25
30
 
@@ -48,7 +53,8 @@ def plot_sample_with_patches(s, marker, patchmeta, remove_margin=False, ax=None,
48
53
  ax.set_ylim(y_max, y_min)
49
54
 
50
55
  if show:
51
- plt.show()
56
+ settings.show()
57
+ return ax.figure
52
58
 
53
59
 
54
60
  def plot_samples_with_patches(samples, marker, patchmeta, ncols=5, **kwargs):
@@ -63,6 +69,11 @@ def plot_samples_with_patches(samples, marker, patchmeta, ncols=5, **kwargs):
63
69
  Marker to display.
64
70
  patchmeta
65
71
  Patch metadata; patches are outlined on their sample.
72
+
73
+ Returns
74
+ -------
75
+ matplotlib.figure.Figure
76
+ The figure that was drawn.
66
77
  """
67
78
  nrows = int(np.ceil(len(samples) / ncols))
68
79
  fig, axs = plt.subplots(nrows, ncols, figsize=(3*ncols, 3*nrows))
@@ -70,7 +81,8 @@ def plot_samples_with_patches(samples, marker, patchmeta, ncols=5, **kwargs):
70
81
  plot_sample_with_patches(s, marker, patchmeta, ax=ax, show=False, **kwargs)
71
82
  ax.set_title(s.sid)
72
83
  fig.tight_layout()
73
- fig.show()
84
+ settings.show(fig=fig)
85
+ return fig
74
86
 
75
87
 
76
88
  def plot_npatches_per_sample(samples, patchmeta):
@@ -79,17 +91,23 @@ def plot_npatches_per_sample(samples, patchmeta):
79
91
 
80
92
  Samples in `samples` with no patches in `patchmeta` are shown with a count
81
93
  of zero.
94
+
95
+ Returns
96
+ -------
97
+ matplotlib.figure.Figure
98
+ The figure that was drawn.
82
99
  """
83
100
  res = patchmeta.sid.value_counts()
84
101
  empty = [sid for sid in samples.keys() if sid not in patchmeta.sid.unique()]
85
102
  for sid in empty:
86
103
  res.loc[sid] = 0
87
104
 
88
- plt.figure(figsize=(15,2))
105
+ fig = plt.figure(figsize=(15,2))
89
106
  plt.bar(x=res.index, height=res)
90
107
  plt.tick_params(axis='x', rotation=90)
91
108
  plt.gca().spines[['top', 'right']].set_visible(False)
92
- plt.show()
109
+ settings.show()
110
+ return fig
93
111
 
94
112
 
95
113
  def _adjust_resolution(mypatches):
@@ -185,7 +203,7 @@ def spatialplot(patchmeta, values, sids=None, cmap='viridis', vmin=None, vmax=No
185
203
  fig._vima_sid_to_ax = sid_to_ax
186
204
  fig.tight_layout()
187
205
  if show:
188
- plt.show()
206
+ settings.show()
189
207
  return fig
190
208
 
191
209
 
@@ -205,7 +223,7 @@ def annotate_spatialplot(patchmeta, highlight, color, thickness=3, show=True, fi
205
223
  Figure returned by `spatialplot`; defaults to the current figure.
206
224
  """
207
225
  if fig is None:
208
- fig = plt.gcf()
226
+ fig = settings.current_figure()
209
227
  sid_to_ax = fig._vima_sid_to_ax
210
228
  for sid, ax in sid_to_ax.items():
211
229
  mypatches = patchmeta[patchmeta.sid == sid]
@@ -234,5 +252,5 @@ def annotate_spatialplot(patchmeta, highlight, color, thickness=3, show=True, fi
234
252
  ax.plot(np.append(cnt[:, 0], cnt[0, 0]), np.append(cnt[:, 1], cnt[0, 1]), color=color, linewidth=thickness)
235
253
 
236
254
  if show:
237
- plt.show()
255
+ settings.show(fig=fig)
238
256
  return fig
@@ -1,5 +1,6 @@
1
1
  import matplotlib.pyplot as plt
2
2
  import scanpy as sc
3
+ from .._settings import settings
3
4
 
4
5
 
5
6
  def plot_association(D, key='mncoef', fdr_thresh=0.1, ax=None, show=True, **kwargs):
@@ -17,6 +18,11 @@ def plot_association(D, key='mncoef', fdr_thresh=0.1, ax=None, show=True, **kwar
17
18
  Coefficient column in ``D.obs`` (with matching ``{key}_fdr``).
18
19
  fdr_thresh
19
20
  FDR cutoff for calling a microniche significant.
21
+
22
+ Returns
23
+ -------
24
+ matplotlib.figure.Figure
25
+ The figure that was drawn.
20
26
  """
21
27
  if ax is None:
22
28
  ax = plt.gca()
@@ -34,4 +40,5 @@ def plot_association(D, key='mncoef', fdr_thresh=0.1, ax=None, show=True, **kwar
34
40
  plt.title(f'No significant microniches at FDR {fdr_thresh*100:.0f}%')
35
41
 
36
42
  if show:
37
- plt.show()
43
+ settings.show()
44
+ return ax.figure
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vima-spatial
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: variational inference-based microniche analysis
5
5
  Home-page: https://github.com/yakirr/vima
6
6
  Author: Yakir Reshef
File without changes