spatialcore 0.1.9__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 (36) hide show
  1. spatialcore/__init__.py +122 -0
  2. spatialcore/annotation/__init__.py +253 -0
  3. spatialcore/annotation/acquisition.py +529 -0
  4. spatialcore/annotation/annotate.py +603 -0
  5. spatialcore/annotation/cellxgene.py +365 -0
  6. spatialcore/annotation/confidence.py +802 -0
  7. spatialcore/annotation/discovery.py +529 -0
  8. spatialcore/annotation/expression.py +363 -0
  9. spatialcore/annotation/loading.py +529 -0
  10. spatialcore/annotation/markers.py +297 -0
  11. spatialcore/annotation/ontology.py +1282 -0
  12. spatialcore/annotation/patterns.py +247 -0
  13. spatialcore/annotation/pipeline.py +620 -0
  14. spatialcore/annotation/synapse.py +380 -0
  15. spatialcore/annotation/training.py +1457 -0
  16. spatialcore/annotation/validation.py +422 -0
  17. spatialcore/core/__init__.py +34 -0
  18. spatialcore/core/cache.py +118 -0
  19. spatialcore/core/logging.py +135 -0
  20. spatialcore/core/metadata.py +149 -0
  21. spatialcore/core/utils.py +768 -0
  22. spatialcore/data/gene_mappings/ensembl_to_hugo_human.tsv +86372 -0
  23. spatialcore/data/markers/canonical_markers.json +83 -0
  24. spatialcore/data/ontology_mappings/ontology_index.json +63865 -0
  25. spatialcore/plotting/__init__.py +109 -0
  26. spatialcore/plotting/benchmark.py +477 -0
  27. spatialcore/plotting/celltype.py +329 -0
  28. spatialcore/plotting/confidence.py +413 -0
  29. spatialcore/plotting/spatial.py +505 -0
  30. spatialcore/plotting/utils.py +411 -0
  31. spatialcore/plotting/validation.py +1342 -0
  32. spatialcore-0.1.9.dist-info/METADATA +213 -0
  33. spatialcore-0.1.9.dist-info/RECORD +36 -0
  34. spatialcore-0.1.9.dist-info/WHEEL +5 -0
  35. spatialcore-0.1.9.dist-info/licenses/LICENSE +201 -0
  36. spatialcore-0.1.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,411 @@
1
+ """
2
+ Plotting utilities for SpatialCore visualization.
3
+
4
+ This module provides utility functions for:
5
+ 1. Color palette generation and loading
6
+ 2. Figure setup and configuration
7
+ 3. Saving figures in multiple formats
8
+
9
+ These utilities are used by all other plotting modules.
10
+ """
11
+
12
+ from pathlib import Path
13
+ from typing import Dict, List, Optional, Tuple, Union
14
+ import json
15
+
16
+ import numpy as np
17
+ import matplotlib.pyplot as plt
18
+ from matplotlib.figure import Figure
19
+ from matplotlib.axes import Axes
20
+
21
+ from spatialcore.core.logging import get_logger
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ # ============================================================================
27
+ # Color Palettes
28
+ # ============================================================================
29
+
30
+ # Default high-contrast palette for dark backgrounds
31
+ DEFAULT_PALETTE = [
32
+ "#F5252F", # Red
33
+ "#FB3FFC", # Magenta
34
+ "#00FFFF", # Cyan
35
+ "#33FF33", # Green
36
+ "#FFB300", # Amber
37
+ "#9966FF", # Purple
38
+ "#FF6B6B", # Coral
39
+ "#3784FE", # Blue
40
+ "#FF8000", # Orange
41
+ "#66CCCC", # Teal
42
+ "#CC66FF", # Lavender
43
+ "#99FF99", # Light green
44
+ "#FF6699", # Pink
45
+ "#E3E1E3", # Light gray
46
+ "#FFB3CC", # Light pink
47
+ "#E6E680", # Yellow-green
48
+ "#CC9966", # Tan
49
+ "#8080FF", # Periwinkle
50
+ "#FF9999", # Salmon
51
+ "#66FF99", # Mint
52
+ ]
53
+
54
+ # Colorblind-safe palette (from ColorBrewer)
55
+ COLORBLIND_PALETTE = [
56
+ "#E69F00", # Orange
57
+ "#56B4E9", # Sky blue
58
+ "#009E73", # Bluish green
59
+ "#F0E442", # Yellow
60
+ "#0072B2", # Blue
61
+ "#D55E00", # Vermillion
62
+ "#CC79A7", # Reddish purple
63
+ "#000000", # Black
64
+ ]
65
+
66
+
67
+ def generate_celltype_palette(
68
+ cell_types: List[str],
69
+ palette: str = "default",
70
+ custom_colors: Optional[Dict[str, str]] = None,
71
+ ) -> Dict[str, str]:
72
+ """
73
+ Generate color palette for cell types.
74
+
75
+ Parameters
76
+ ----------
77
+ cell_types : List[str]
78
+ List of cell type names.
79
+ palette : str, default "default"
80
+ Palette name: "default", "colorblind", "tab20", "Set1", "Set2", "Set3".
81
+ custom_colors : Dict[str, str], optional
82
+ Custom color overrides for specific cell types.
83
+
84
+ Returns
85
+ -------
86
+ Dict[str, str]
87
+ Mapping from cell type name to hex color.
88
+
89
+ Examples
90
+ --------
91
+ >>> from spatialcore.plotting.utils import generate_celltype_palette
92
+ >>> colors = generate_celltype_palette(
93
+ ... ["T cell", "B cell", "Macrophage"],
94
+ ... custom_colors={"T cell": "#FF0000"},
95
+ ... )
96
+ """
97
+ # Get base palette
98
+ if palette == "default":
99
+ base_colors = DEFAULT_PALETTE
100
+ elif palette == "colorblind":
101
+ base_colors = COLORBLIND_PALETTE
102
+ elif palette in ["tab20", "Set1", "Set2", "Set3"]:
103
+ cmap = plt.get_cmap(palette)
104
+ n_colors = cmap.N if hasattr(cmap, "N") else 20
105
+ base_colors = [plt.colors.rgb2hex(cmap(i)) for i in range(n_colors)]
106
+ else:
107
+ logger.warning(f"Unknown palette '{palette}', using default")
108
+ base_colors = DEFAULT_PALETTE
109
+
110
+ if custom_colors is None:
111
+ custom_colors = {}
112
+
113
+ color_map = {}
114
+ color_idx = 0
115
+
116
+ for cell_type in sorted(cell_types):
117
+ if cell_type in custom_colors:
118
+ color_map[cell_type] = custom_colors[cell_type]
119
+ else:
120
+ color_map[cell_type] = base_colors[color_idx % len(base_colors)]
121
+ color_idx += 1
122
+
123
+ return color_map
124
+
125
+
126
+ def load_celltype_palette(path: Path) -> Dict[str, str]:
127
+ """
128
+ Load cell type color palette from JSON file.
129
+
130
+ Parameters
131
+ ----------
132
+ path : Path
133
+ Path to JSON file with color mapping.
134
+
135
+ Returns
136
+ -------
137
+ Dict[str, str]
138
+ Mapping from cell type name to hex color.
139
+ """
140
+ path = Path(path)
141
+ if not path.exists():
142
+ raise FileNotFoundError(f"Palette file not found: {path}")
143
+
144
+ with open(path, "r") as f:
145
+ colors = json.load(f)
146
+
147
+ logger.info(f"Loaded {len(colors)} colors from {path}")
148
+ return colors
149
+
150
+
151
+ def save_celltype_palette(
152
+ colors: Dict[str, str],
153
+ path: Path,
154
+ ) -> None:
155
+ """
156
+ Save cell type color palette to JSON file.
157
+
158
+ Parameters
159
+ ----------
160
+ colors : Dict[str, str]
161
+ Mapping from cell type name to hex color.
162
+ path : Path
163
+ Output path for JSON file.
164
+ """
165
+ path = Path(path)
166
+ path.parent.mkdir(parents=True, exist_ok=True)
167
+
168
+ with open(path, "w") as f:
169
+ json.dump(colors, f, indent=2)
170
+
171
+ logger.info(f"Saved {len(colors)} colors to {path}")
172
+
173
+
174
+ # ============================================================================
175
+ # Figure Setup
176
+ # ============================================================================
177
+
178
+ def setup_figure(
179
+ figsize: Tuple[float, float] = (8, 6),
180
+ dpi: int = 150,
181
+ style: str = "ticks",
182
+ context: str = "notebook",
183
+ dark_background: bool = False,
184
+ ) -> Tuple[Figure, Axes]:
185
+ """
186
+ Create figure with consistent styling.
187
+
188
+ Parameters
189
+ ----------
190
+ figsize : Tuple[float, float], default (8, 6)
191
+ Figure size in inches (width, height).
192
+ dpi : int, default 150
193
+ Dots per inch for rendering.
194
+ style : str, default "ticks"
195
+ Seaborn style: "ticks", "whitegrid", "darkgrid", "white", "dark".
196
+ context : str, default "notebook"
197
+ Seaborn context: "paper", "notebook", "talk", "poster".
198
+ dark_background : bool, default False
199
+ Use dark background for spatial plots.
200
+
201
+ Returns
202
+ -------
203
+ Tuple[Figure, Axes]
204
+ Matplotlib figure and axes.
205
+
206
+ Examples
207
+ --------
208
+ >>> from spatialcore.plotting.utils import setup_figure
209
+ >>> fig, ax = setup_figure(figsize=(10, 8), dpi=300)
210
+ >>> ax.scatter(x, y)
211
+ >>> fig.savefig("plot.png")
212
+ """
213
+ try:
214
+ import seaborn as sns
215
+ sns.set_style(style)
216
+ sns.set_context(context)
217
+ except ImportError:
218
+ pass # Seaborn optional
219
+
220
+ if dark_background:
221
+ plt.style.use("dark_background")
222
+
223
+ fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
224
+ return fig, ax
225
+
226
+
227
+ def setup_multi_figure(
228
+ nrows: int = 1,
229
+ ncols: int = 1,
230
+ figsize: Optional[Tuple[float, float]] = None,
231
+ dpi: int = 150,
232
+ sharex: bool = False,
233
+ sharey: bool = False,
234
+ ) -> Tuple[Figure, np.ndarray]:
235
+ """
236
+ Create multi-panel figure.
237
+
238
+ Parameters
239
+ ----------
240
+ nrows : int, default 1
241
+ Number of subplot rows.
242
+ ncols : int, default 1
243
+ Number of subplot columns.
244
+ figsize : Tuple[float, float], optional
245
+ Figure size. If None, auto-calculated from panel count.
246
+ dpi : int, default 150
247
+ Dots per inch.
248
+ sharex : bool, default False
249
+ Share x-axis across subplots.
250
+ sharey : bool, default False
251
+ Share y-axis across subplots.
252
+
253
+ Returns
254
+ -------
255
+ Tuple[Figure, np.ndarray]
256
+ Figure and array of axes.
257
+ """
258
+ if figsize is None:
259
+ figsize = (4 * ncols, 4 * nrows)
260
+
261
+ fig, axes = plt.subplots(
262
+ nrows=nrows,
263
+ ncols=ncols,
264
+ figsize=figsize,
265
+ dpi=dpi,
266
+ sharex=sharex,
267
+ sharey=sharey,
268
+ )
269
+
270
+ return fig, np.atleast_2d(axes) if nrows == 1 or ncols == 1 else axes
271
+
272
+
273
+ # ============================================================================
274
+ # Figure Saving
275
+ # ============================================================================
276
+
277
+ def save_figure(
278
+ fig: Figure,
279
+ path: Union[str, Path],
280
+ formats: List[str] = None,
281
+ dpi: int = 300,
282
+ bbox_inches: str = "tight",
283
+ transparent: bool = False,
284
+ ) -> List[Path]:
285
+ """
286
+ Save figure in multiple formats.
287
+
288
+ Parameters
289
+ ----------
290
+ fig : Figure
291
+ Matplotlib figure to save.
292
+ path : str or Path
293
+ Base output path (without extension).
294
+ formats : List[str], optional
295
+ Output formats. Default: ["png"].
296
+ dpi : int, default 300
297
+ Resolution for raster formats.
298
+ bbox_inches : str, default "tight"
299
+ Bounding box setting.
300
+ transparent : bool, default False
301
+ Transparent background.
302
+
303
+ Returns
304
+ -------
305
+ List[Path]
306
+ Paths to saved files.
307
+
308
+ Examples
309
+ --------
310
+ >>> from spatialcore.plotting.utils import save_figure
311
+ >>> paths = save_figure(fig, "output/my_plot", formats=["png", "pdf", "svg"])
312
+ """
313
+ if formats is None:
314
+ formats = ["png"]
315
+
316
+ path = Path(path)
317
+ path.parent.mkdir(parents=True, exist_ok=True)
318
+
319
+ saved_paths = []
320
+ for fmt in formats:
321
+ output_path = path.with_suffix(f".{fmt}")
322
+ fig.savefig(
323
+ output_path,
324
+ format=fmt,
325
+ dpi=dpi,
326
+ bbox_inches=bbox_inches,
327
+ transparent=transparent,
328
+ )
329
+ saved_paths.append(output_path)
330
+ logger.debug(f"Saved figure to: {output_path}")
331
+
332
+ logger.info(f"Saved figure to {len(saved_paths)} formats: {path}")
333
+ return saved_paths
334
+
335
+
336
+ def close_figure(fig: Figure) -> None:
337
+ """
338
+ Close figure to free memory.
339
+
340
+ Parameters
341
+ ----------
342
+ fig : Figure
343
+ Matplotlib figure to close.
344
+ """
345
+ plt.close(fig)
346
+
347
+
348
+ # ============================================================================
349
+ # Axis Formatting
350
+ # ============================================================================
351
+
352
+ def format_axis_labels(
353
+ ax: Axes,
354
+ xlabel: Optional[str] = None,
355
+ ylabel: Optional[str] = None,
356
+ title: Optional[str] = None,
357
+ fontsize: int = 12,
358
+ ) -> Axes:
359
+ """
360
+ Format axis labels and title.
361
+
362
+ Parameters
363
+ ----------
364
+ ax : Axes
365
+ Matplotlib axes.
366
+ xlabel : str, optional
367
+ X-axis label.
368
+ ylabel : str, optional
369
+ Y-axis label.
370
+ title : str, optional
371
+ Plot title.
372
+ fontsize : int, default 12
373
+ Font size for labels.
374
+
375
+ Returns
376
+ -------
377
+ Axes
378
+ Modified axes.
379
+ """
380
+ if xlabel:
381
+ ax.set_xlabel(xlabel, fontsize=fontsize)
382
+ if ylabel:
383
+ ax.set_ylabel(ylabel, fontsize=fontsize)
384
+ if title:
385
+ ax.set_title(title, fontsize=fontsize + 2)
386
+ return ax
387
+
388
+
389
+ def despine(ax: Axes, top: bool = True, right: bool = True) -> Axes:
390
+ """
391
+ Remove spines from axes.
392
+
393
+ Parameters
394
+ ----------
395
+ ax : Axes
396
+ Matplotlib axes.
397
+ top : bool, default True
398
+ Remove top spine.
399
+ right : bool, default True
400
+ Remove right spine.
401
+
402
+ Returns
403
+ -------
404
+ Axes
405
+ Modified axes.
406
+ """
407
+ if top:
408
+ ax.spines["top"].set_visible(False)
409
+ if right:
410
+ ax.spines["right"].set_visible(False)
411
+ return ax