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,505 @@
1
+ """
2
+ Spatial visualization of cell types and confidence.
3
+
4
+ This module provides functions for visualizing cell annotations
5
+ on spatial coordinates.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Dict, List, Optional, Tuple, Union
10
+
11
+ import numpy as np
12
+ import matplotlib.pyplot as plt
13
+ from matplotlib.figure import Figure
14
+ from matplotlib.colors import Normalize
15
+ import matplotlib.cm as cm
16
+ import anndata as ad
17
+
18
+ from spatialcore.core.logging import get_logger
19
+ from spatialcore.plotting.utils import (
20
+ generate_celltype_palette,
21
+ setup_figure,
22
+ save_figure,
23
+ format_axis_labels,
24
+ )
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ def plot_spatial_celltype(
30
+ adata: ad.AnnData,
31
+ label_column: str,
32
+ spatial_key: str = "spatial",
33
+ colors: Optional[Dict[str, str]] = None,
34
+ point_size: float = 1.0,
35
+ alpha: float = 0.8,
36
+ figsize: tuple = (10, 10),
37
+ dark_background: bool = True,
38
+ legend_loc: str = "right margin",
39
+ xlim: Optional[Tuple[float, float]] = None,
40
+ ylim: Optional[Tuple[float, float]] = None,
41
+ title: Optional[str] = None,
42
+ save: Optional[Union[str, Path]] = None,
43
+ ) -> Figure:
44
+ """
45
+ Plot cell types on spatial coordinates.
46
+
47
+ Parameters
48
+ ----------
49
+ adata : AnnData
50
+ Annotated data with spatial coordinates.
51
+ label_column : str
52
+ Column in adata.obs containing cell type labels.
53
+ spatial_key : str, default "spatial"
54
+ Key in adata.obsm for spatial coordinates.
55
+ colors : Dict[str, str], optional
56
+ Color mapping for cell types.
57
+ point_size : float, default 1.0
58
+ Size of points.
59
+ alpha : float, default 0.8
60
+ Point transparency.
61
+ figsize : tuple, default (10, 10)
62
+ Figure size.
63
+ dark_background : bool, default True
64
+ Use dark background (better for spatial data).
65
+ legend_loc : str, default "right margin"
66
+ Legend location: "right margin", "on data", "none".
67
+ xlim : Tuple[float, float], optional
68
+ X-axis limits.
69
+ ylim : Tuple[float, float], optional
70
+ Y-axis limits.
71
+ title : str, optional
72
+ Plot title.
73
+ save : str or Path, optional
74
+ Path to save figure.
75
+
76
+ Returns
77
+ -------
78
+ Figure
79
+ Matplotlib figure.
80
+
81
+ Examples
82
+ --------
83
+ >>> from spatialcore.plotting.spatial import plot_spatial_celltype
84
+ >>> fig = plot_spatial_celltype(
85
+ ... adata,
86
+ ... label_column="cell_type",
87
+ ... point_size=0.5,
88
+ ... )
89
+ """
90
+ if label_column not in adata.obs.columns:
91
+ raise ValueError(f"Label column '{label_column}' not found.")
92
+
93
+ if spatial_key not in adata.obsm:
94
+ raise ValueError(
95
+ f"Spatial key '{spatial_key}' not found. "
96
+ f"Available: {list(adata.obsm.keys())}"
97
+ )
98
+
99
+ coords = adata.obsm[spatial_key]
100
+ cell_types = adata.obs[label_column].astype(str)
101
+ unique_types = sorted(cell_types.unique())
102
+
103
+ if colors is None:
104
+ colors = generate_celltype_palette(unique_types)
105
+
106
+ # Adjust figure size for legend
107
+ if legend_loc == "right margin":
108
+ figsize = (figsize[0] + 3, figsize[1])
109
+
110
+ if dark_background:
111
+ plt.style.use("dark_background")
112
+
113
+ fig, ax = setup_figure(figsize=figsize)
114
+
115
+ # Plot each cell type
116
+ for cell_type in unique_types:
117
+ mask = cell_types == cell_type
118
+ ax.scatter(
119
+ coords[mask, 0],
120
+ coords[mask, 1],
121
+ c=colors.get(cell_type, "#888888"),
122
+ label=cell_type,
123
+ s=point_size,
124
+ alpha=alpha,
125
+ rasterized=True,
126
+ )
127
+
128
+ ax.set_xlabel("X")
129
+ ax.set_ylabel("Y")
130
+ ax.set_aspect("equal")
131
+
132
+ if xlim:
133
+ ax.set_xlim(xlim)
134
+ if ylim:
135
+ ax.set_ylim(ylim)
136
+
137
+ # Legend
138
+ if legend_loc == "right margin":
139
+ ax.legend(
140
+ bbox_to_anchor=(1.02, 1),
141
+ loc="upper left",
142
+ markerscale=5,
143
+ frameon=False,
144
+ )
145
+ elif legend_loc == "on data":
146
+ ax.legend(loc="best", markerscale=5)
147
+
148
+ if title is None:
149
+ title = f"Spatial Cell Types ({label_column})"
150
+ ax.set_title(title, color="white" if dark_background else "black")
151
+
152
+ plt.tight_layout()
153
+
154
+ if save:
155
+ save_figure(fig, save)
156
+
157
+ # Reset style
158
+ if dark_background:
159
+ plt.style.use("default")
160
+
161
+ return fig
162
+
163
+
164
+ def plot_spatial_confidence(
165
+ adata: ad.AnnData,
166
+ confidence_column: str,
167
+ spatial_key: str = "spatial",
168
+ cmap: str = "viridis",
169
+ point_size: float = 1.0,
170
+ alpha: float = 0.8,
171
+ vmin: Optional[float] = None,
172
+ vmax: Optional[float] = None,
173
+ figsize: tuple = (10, 10),
174
+ dark_background: bool = True,
175
+ colorbar: bool = True,
176
+ xlim: Optional[Tuple[float, float]] = None,
177
+ ylim: Optional[Tuple[float, float]] = None,
178
+ title: Optional[str] = None,
179
+ save: Optional[Union[str, Path]] = None,
180
+ ) -> Figure:
181
+ """
182
+ Plot confidence scores on spatial coordinates.
183
+
184
+ Parameters
185
+ ----------
186
+ adata : AnnData
187
+ Annotated data with spatial coordinates and confidence.
188
+ confidence_column : str
189
+ Column in adata.obs containing confidence values.
190
+ spatial_key : str, default "spatial"
191
+ Key in adata.obsm for spatial coordinates.
192
+ cmap : str, default "viridis"
193
+ Colormap name.
194
+ point_size : float, default 1.0
195
+ Size of points.
196
+ alpha : float, default 0.8
197
+ Point transparency.
198
+ vmin : float, optional
199
+ Minimum value for color scale.
200
+ vmax : float, optional
201
+ Maximum value for color scale.
202
+ figsize : tuple, default (10, 10)
203
+ Figure size.
204
+ dark_background : bool, default True
205
+ Use dark background.
206
+ colorbar : bool, default True
207
+ Show colorbar.
208
+ xlim : Tuple[float, float], optional
209
+ X-axis limits.
210
+ ylim : Tuple[float, float], optional
211
+ Y-axis limits.
212
+ title : str, optional
213
+ Plot title.
214
+ save : str or Path, optional
215
+ Path to save figure.
216
+
217
+ Returns
218
+ -------
219
+ Figure
220
+ Matplotlib figure.
221
+
222
+ Examples
223
+ --------
224
+ >>> from spatialcore.plotting.spatial import plot_spatial_confidence
225
+ >>> fig = plot_spatial_confidence(
226
+ ... adata,
227
+ ... confidence_column="confidence",
228
+ ... cmap="plasma",
229
+ ... )
230
+ """
231
+ if confidence_column not in adata.obs.columns:
232
+ raise ValueError(f"Confidence column '{confidence_column}' not found.")
233
+
234
+ if spatial_key not in adata.obsm:
235
+ raise ValueError(f"Spatial key '{spatial_key}' not found.")
236
+
237
+ coords = adata.obsm[spatial_key]
238
+ confidence = adata.obs[confidence_column].values
239
+
240
+ if dark_background:
241
+ plt.style.use("dark_background")
242
+
243
+ fig, ax = setup_figure(figsize=figsize)
244
+
245
+ scatter = ax.scatter(
246
+ coords[:, 0],
247
+ coords[:, 1],
248
+ c=confidence,
249
+ cmap=cmap,
250
+ s=point_size,
251
+ alpha=alpha,
252
+ vmin=vmin,
253
+ vmax=vmax,
254
+ rasterized=True,
255
+ )
256
+
257
+ ax.set_xlabel("X")
258
+ ax.set_ylabel("Y")
259
+ ax.set_aspect("equal")
260
+
261
+ if xlim:
262
+ ax.set_xlim(xlim)
263
+ if ylim:
264
+ ax.set_ylim(ylim)
265
+
266
+ if colorbar:
267
+ cbar = plt.colorbar(scatter, ax=ax, shrink=0.6)
268
+ cbar.set_label("Confidence", fontsize=10)
269
+
270
+ if title is None:
271
+ title = f"Spatial Confidence ({confidence_column})"
272
+ ax.set_title(title, color="white" if dark_background else "black")
273
+
274
+ plt.tight_layout()
275
+
276
+ if save:
277
+ save_figure(fig, save)
278
+
279
+ if dark_background:
280
+ plt.style.use("default")
281
+
282
+ return fig
283
+
284
+
285
+ def plot_spatial_gene(
286
+ adata: ad.AnnData,
287
+ gene: str,
288
+ spatial_key: str = "spatial",
289
+ layer: Optional[str] = None,
290
+ cmap: str = "Reds",
291
+ point_size: float = 1.0,
292
+ alpha: float = 0.8,
293
+ vmin: Optional[float] = None,
294
+ vmax: Optional[float] = None,
295
+ figsize: tuple = (10, 10),
296
+ dark_background: bool = True,
297
+ colorbar: bool = True,
298
+ title: Optional[str] = None,
299
+ save: Optional[Union[str, Path]] = None,
300
+ ) -> Figure:
301
+ """
302
+ Plot gene expression on spatial coordinates.
303
+
304
+ Parameters
305
+ ----------
306
+ adata : AnnData
307
+ Annotated data with spatial coordinates.
308
+ gene : str
309
+ Gene name to plot.
310
+ spatial_key : str, default "spatial"
311
+ Key in adata.obsm for spatial coordinates.
312
+ layer : str, optional
313
+ Layer to use. If None, uses adata.X.
314
+ cmap : str, default "Reds"
315
+ Colormap name.
316
+ point_size : float, default 1.0
317
+ Size of points.
318
+ alpha : float, default 0.8
319
+ Point transparency.
320
+ vmin : float, optional
321
+ Minimum value for color scale.
322
+ vmax : float, optional
323
+ Maximum value for color scale.
324
+ figsize : tuple, default (10, 10)
325
+ Figure size.
326
+ dark_background : bool, default True
327
+ Use dark background.
328
+ colorbar : bool, default True
329
+ Show colorbar.
330
+ title : str, optional
331
+ Plot title.
332
+ save : str or Path, optional
333
+ Path to save figure.
334
+
335
+ Returns
336
+ -------
337
+ Figure
338
+ Matplotlib figure.
339
+ """
340
+ if gene not in adata.var_names:
341
+ raise ValueError(
342
+ f"Gene '{gene}' not found. "
343
+ f"Available genes: {list(adata.var_names[:10])}..."
344
+ )
345
+
346
+ if spatial_key not in adata.obsm:
347
+ raise ValueError(f"Spatial key '{spatial_key}' not found.")
348
+
349
+ coords = adata.obsm[spatial_key]
350
+
351
+ # Get expression
352
+ gene_idx = adata.var_names.get_loc(gene)
353
+ if layer is not None:
354
+ expression = adata.layers[layer][:, gene_idx]
355
+ else:
356
+ X = adata.X
357
+ if hasattr(X, "toarray"):
358
+ expression = X[:, gene_idx].toarray().flatten()
359
+ else:
360
+ expression = X[:, gene_idx].flatten()
361
+
362
+ if dark_background:
363
+ plt.style.use("dark_background")
364
+
365
+ fig, ax = setup_figure(figsize=figsize)
366
+
367
+ scatter = ax.scatter(
368
+ coords[:, 0],
369
+ coords[:, 1],
370
+ c=expression,
371
+ cmap=cmap,
372
+ s=point_size,
373
+ alpha=alpha,
374
+ vmin=vmin,
375
+ vmax=vmax,
376
+ rasterized=True,
377
+ )
378
+
379
+ ax.set_xlabel("X")
380
+ ax.set_ylabel("Y")
381
+ ax.set_aspect("equal")
382
+
383
+ if colorbar:
384
+ cbar = plt.colorbar(scatter, ax=ax, shrink=0.6)
385
+ cbar.set_label("Expression", fontsize=10)
386
+
387
+ if title is None:
388
+ title = f"{gene} Expression"
389
+ ax.set_title(title, color="white" if dark_background else "black")
390
+
391
+ plt.tight_layout()
392
+
393
+ if save:
394
+ save_figure(fig, save)
395
+
396
+ if dark_background:
397
+ plt.style.use("default")
398
+
399
+ return fig
400
+
401
+
402
+ def plot_spatial_multi_gene(
403
+ adata: ad.AnnData,
404
+ genes: List[str],
405
+ spatial_key: str = "spatial",
406
+ layer: Optional[str] = None,
407
+ cmap: str = "Reds",
408
+ point_size: float = 0.5,
409
+ ncols: int = 3,
410
+ figsize_per_panel: Tuple[float, float] = (4, 4),
411
+ dark_background: bool = True,
412
+ save: Optional[Union[str, Path]] = None,
413
+ ) -> Figure:
414
+ """
415
+ Plot multiple genes on spatial coordinates.
416
+
417
+ Parameters
418
+ ----------
419
+ adata : AnnData
420
+ Annotated data with spatial coordinates.
421
+ genes : List[str]
422
+ List of gene names to plot.
423
+ spatial_key : str, default "spatial"
424
+ Key in adata.obsm for spatial coordinates.
425
+ layer : str, optional
426
+ Layer to use.
427
+ cmap : str, default "Reds"
428
+ Colormap name.
429
+ point_size : float, default 0.5
430
+ Size of points.
431
+ ncols : int, default 3
432
+ Number of columns in subplot grid.
433
+ figsize_per_panel : Tuple[float, float], default (4, 4)
434
+ Size per panel.
435
+ dark_background : bool, default True
436
+ Use dark background.
437
+ save : str or Path, optional
438
+ Path to save figure.
439
+
440
+ Returns
441
+ -------
442
+ Figure
443
+ Matplotlib figure.
444
+ """
445
+ # Filter to available genes
446
+ available_genes = [g for g in genes if g in adata.var_names]
447
+ missing = set(genes) - set(available_genes)
448
+ if missing:
449
+ logger.warning(f"Genes not found: {missing}")
450
+
451
+ if not available_genes:
452
+ raise ValueError("No valid genes found.")
453
+
454
+ n_genes = len(available_genes)
455
+ nrows = int(np.ceil(n_genes / ncols))
456
+
457
+ figsize = (figsize_per_panel[0] * ncols, figsize_per_panel[1] * nrows)
458
+
459
+ if dark_background:
460
+ plt.style.use("dark_background")
461
+
462
+ fig, axes = plt.subplots(nrows, ncols, figsize=figsize)
463
+ axes = np.atleast_2d(axes).flatten()
464
+
465
+ coords = adata.obsm[spatial_key]
466
+
467
+ for i, gene in enumerate(available_genes):
468
+ ax = axes[i]
469
+
470
+ gene_idx = adata.var_names.get_loc(gene)
471
+ if layer is not None:
472
+ expression = adata.layers[layer][:, gene_idx]
473
+ else:
474
+ X = adata.X
475
+ if hasattr(X, "toarray"):
476
+ expression = X[:, gene_idx].toarray().flatten()
477
+ else:
478
+ expression = X[:, gene_idx].flatten()
479
+
480
+ ax.scatter(
481
+ coords[:, 0],
482
+ coords[:, 1],
483
+ c=expression,
484
+ cmap=cmap,
485
+ s=point_size,
486
+ rasterized=True,
487
+ )
488
+ ax.set_aspect("equal")
489
+ ax.set_title(gene, fontsize=10)
490
+ ax.set_xticks([])
491
+ ax.set_yticks([])
492
+
493
+ # Hide empty panels
494
+ for i in range(n_genes, len(axes)):
495
+ axes[i].set_visible(False)
496
+
497
+ plt.tight_layout()
498
+
499
+ if save:
500
+ save_figure(fig, save)
501
+
502
+ if dark_background:
503
+ plt.style.use("default")
504
+
505
+ return fig