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,329 @@
1
+ """
2
+ Cell type distribution and UMAP visualization.
3
+
4
+ This module provides functions for visualizing cell type distributions
5
+ and embeddings.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Dict, List, Optional, Union
10
+
11
+ import numpy as np
12
+ import pandas as pd
13
+ import matplotlib.pyplot as plt
14
+ from matplotlib.figure import Figure
15
+ import anndata as ad
16
+
17
+ from spatialcore.core.logging import get_logger
18
+ from spatialcore.plotting.utils import (
19
+ generate_celltype_palette,
20
+ setup_figure,
21
+ save_figure,
22
+ despine,
23
+ format_axis_labels,
24
+ )
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ def plot_celltype_distribution(
30
+ adata: ad.AnnData,
31
+ label_column: str,
32
+ colors: Optional[Dict[str, str]] = None,
33
+ horizontal: bool = False,
34
+ top_n: Optional[int] = None,
35
+ figsize: Optional[tuple] = None,
36
+ title: Optional[str] = None,
37
+ save: Optional[Union[str, Path]] = None,
38
+ ) -> Figure:
39
+ """
40
+ Plot cell type distribution as bar chart.
41
+
42
+ Parameters
43
+ ----------
44
+ adata : AnnData
45
+ Annotated data with cell type labels.
46
+ label_column : str
47
+ Column in adata.obs containing cell type labels.
48
+ colors : Dict[str, str], optional
49
+ Color mapping for cell types. If None, auto-generated.
50
+ horizontal : bool, default False
51
+ Plot horizontal bars (easier to read with many types).
52
+ top_n : int, optional
53
+ Only show top N most frequent cell types.
54
+ figsize : tuple, optional
55
+ Figure size. Auto-calculated if None.
56
+ title : str, optional
57
+ Plot title. Default: "Cell Type Distribution".
58
+ save : str or Path, optional
59
+ Path to save figure (without extension).
60
+
61
+ Returns
62
+ -------
63
+ Figure
64
+ Matplotlib figure.
65
+
66
+ Examples
67
+ --------
68
+ >>> from spatialcore.plotting.celltype import plot_celltype_distribution
69
+ >>> fig = plot_celltype_distribution(
70
+ ... adata,
71
+ ... label_column="cell_type",
72
+ ... horizontal=True,
73
+ ... top_n=20,
74
+ ... )
75
+ """
76
+ if label_column not in adata.obs.columns:
77
+ raise ValueError(
78
+ f"Label column '{label_column}' not found. "
79
+ f"Available: {list(adata.obs.columns)}"
80
+ )
81
+
82
+ # Get counts
83
+ counts = adata.obs[label_column].value_counts()
84
+
85
+ if top_n is not None:
86
+ counts = counts.head(top_n)
87
+
88
+ cell_types = counts.index.tolist()
89
+ values = counts.values
90
+
91
+ # Generate colors
92
+ if colors is None:
93
+ colors = generate_celltype_palette(cell_types)
94
+
95
+ bar_colors = [colors.get(ct, "#888888") for ct in cell_types]
96
+
97
+ # Calculate figure size
98
+ n_types = len(cell_types)
99
+ if figsize is None:
100
+ if horizontal:
101
+ figsize = (8, max(4, 0.3 * n_types))
102
+ else:
103
+ figsize = (max(8, 0.5 * n_types), 6)
104
+
105
+ fig, ax = setup_figure(figsize=figsize)
106
+
107
+ if horizontal:
108
+ y_pos = np.arange(len(cell_types))
109
+ ax.barh(y_pos, values, color=bar_colors)
110
+ ax.set_yticks(y_pos)
111
+ ax.set_yticklabels(cell_types)
112
+ ax.invert_yaxis() # Top to bottom
113
+ format_axis_labels(ax, xlabel="Number of Cells")
114
+ else:
115
+ x_pos = np.arange(len(cell_types))
116
+ ax.bar(x_pos, values, color=bar_colors)
117
+ ax.set_xticks(x_pos)
118
+ ax.set_xticklabels(cell_types, rotation=45, ha="right")
119
+ format_axis_labels(ax, ylabel="Number of Cells")
120
+
121
+ despine(ax)
122
+
123
+ if title is None:
124
+ title = "Cell Type Distribution"
125
+ ax.set_title(title)
126
+
127
+ plt.tight_layout()
128
+
129
+ if save:
130
+ save_figure(fig, save)
131
+
132
+ return fig
133
+
134
+
135
+ def plot_celltype_pie(
136
+ adata: ad.AnnData,
137
+ label_column: str,
138
+ colors: Optional[Dict[str, str]] = None,
139
+ min_pct: float = 2.0,
140
+ other_label: str = "Other",
141
+ figsize: tuple = (8, 8),
142
+ title: Optional[str] = None,
143
+ save: Optional[Union[str, Path]] = None,
144
+ ) -> Figure:
145
+ """
146
+ Plot cell type distribution as pie chart.
147
+
148
+ Parameters
149
+ ----------
150
+ adata : AnnData
151
+ Annotated data with cell type labels.
152
+ label_column : str
153
+ Column in adata.obs containing cell type labels.
154
+ colors : Dict[str, str], optional
155
+ Color mapping for cell types.
156
+ min_pct : float, default 2.0
157
+ Minimum percentage to show as separate slice.
158
+ Smaller types are grouped into "Other".
159
+ other_label : str, default "Other"
160
+ Label for grouped small cell types.
161
+ figsize : tuple, default (8, 8)
162
+ Figure size.
163
+ title : str, optional
164
+ Plot title.
165
+ save : str or Path, optional
166
+ Path to save figure.
167
+
168
+ Returns
169
+ -------
170
+ Figure
171
+ Matplotlib figure.
172
+ """
173
+ if label_column not in adata.obs.columns:
174
+ raise ValueError(f"Label column '{label_column}' not found.")
175
+
176
+ counts = adata.obs[label_column].value_counts()
177
+ pcts = 100 * counts / counts.sum()
178
+
179
+ # Group small types
180
+ main_types = pcts[pcts >= min_pct]
181
+ other_pct = pcts[pcts < min_pct].sum()
182
+
183
+ if other_pct > 0:
184
+ main_types[other_label] = other_pct
185
+
186
+ cell_types = main_types.index.tolist()
187
+ values = main_types.values
188
+
189
+ # Generate colors
190
+ if colors is None:
191
+ colors = generate_celltype_palette(cell_types)
192
+ colors[other_label] = "#888888"
193
+
194
+ pie_colors = [colors.get(ct, "#888888") for ct in cell_types]
195
+
196
+ fig, ax = setup_figure(figsize=figsize)
197
+
198
+ ax.pie(
199
+ values,
200
+ labels=cell_types,
201
+ colors=pie_colors,
202
+ autopct="%1.1f%%",
203
+ pctdistance=0.8,
204
+ )
205
+
206
+ if title is None:
207
+ title = "Cell Type Distribution"
208
+ ax.set_title(title)
209
+
210
+ if save:
211
+ save_figure(fig, save)
212
+
213
+ return fig
214
+
215
+
216
+ def plot_celltype_umap(
217
+ adata: ad.AnnData,
218
+ label_column: str,
219
+ colors: Optional[Dict[str, str]] = None,
220
+ obsm_key: str = "X_umap",
221
+ point_size: float = 1.0,
222
+ alpha: float = 0.7,
223
+ legend_loc: str = "right margin",
224
+ figsize: tuple = (10, 8),
225
+ title: Optional[str] = None,
226
+ save: Optional[Union[str, Path]] = None,
227
+ ) -> Figure:
228
+ """
229
+ Plot cell types on UMAP embedding.
230
+
231
+ Parameters
232
+ ----------
233
+ adata : AnnData
234
+ Annotated data with UMAP coordinates.
235
+ label_column : str
236
+ Column in adata.obs containing cell type labels.
237
+ colors : Dict[str, str], optional
238
+ Color mapping for cell types.
239
+ obsm_key : str, default "X_umap"
240
+ Key in adata.obsm for embedding coordinates.
241
+ point_size : float, default 1.0
242
+ Size of points.
243
+ alpha : float, default 0.7
244
+ Point transparency.
245
+ legend_loc : str, default "right margin"
246
+ Legend location: "right margin", "on data", "none".
247
+ figsize : tuple, default (10, 8)
248
+ Figure size.
249
+ title : str, optional
250
+ Plot title.
251
+ save : str or Path, optional
252
+ Path to save figure.
253
+
254
+ Returns
255
+ -------
256
+ Figure
257
+ Matplotlib figure.
258
+
259
+ Examples
260
+ --------
261
+ >>> from spatialcore.plotting.celltype import plot_celltype_umap
262
+ >>> fig = plot_celltype_umap(
263
+ ... adata,
264
+ ... label_column="cell_type",
265
+ ... point_size=0.5,
266
+ ... )
267
+ """
268
+ if label_column not in adata.obs.columns:
269
+ raise ValueError(f"Label column '{label_column}' not found.")
270
+
271
+ if obsm_key not in adata.obsm:
272
+ raise ValueError(
273
+ f"Embedding '{obsm_key}' not found. "
274
+ f"Available: {list(adata.obsm.keys())}"
275
+ )
276
+
277
+ coords = adata.obsm[obsm_key]
278
+ cell_types = adata.obs[label_column].astype(str)
279
+ unique_types = sorted(cell_types.unique())
280
+
281
+ # Generate colors
282
+ if colors is None:
283
+ colors = generate_celltype_palette(unique_types)
284
+
285
+ # Adjust figure size for legend
286
+ if legend_loc == "right margin":
287
+ figsize = (figsize[0] + 3, figsize[1])
288
+
289
+ fig, ax = setup_figure(figsize=figsize)
290
+
291
+ # Plot each cell type
292
+ for cell_type in unique_types:
293
+ mask = cell_types == cell_type
294
+ ax.scatter(
295
+ coords[mask, 0],
296
+ coords[mask, 1],
297
+ c=colors.get(cell_type, "#888888"),
298
+ label=cell_type,
299
+ s=point_size,
300
+ alpha=alpha,
301
+ rasterized=True, # Faster for large datasets
302
+ )
303
+
304
+ ax.set_xlabel("UMAP1")
305
+ ax.set_ylabel("UMAP2")
306
+ ax.set_aspect("equal")
307
+
308
+ # Legend
309
+ if legend_loc == "right margin":
310
+ ax.legend(
311
+ bbox_to_anchor=(1.02, 1),
312
+ loc="upper left",
313
+ markerscale=5,
314
+ frameon=False,
315
+ )
316
+ elif legend_loc == "on data":
317
+ ax.legend(loc="best", markerscale=5)
318
+ # else: no legend
319
+
320
+ if title is None:
321
+ title = f"Cell Types ({label_column})"
322
+ ax.set_title(title)
323
+
324
+ plt.tight_layout()
325
+
326
+ if save:
327
+ save_figure(fig, save)
328
+
329
+ return fig