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.
- spatialcore/__init__.py +122 -0
- spatialcore/annotation/__init__.py +253 -0
- spatialcore/annotation/acquisition.py +529 -0
- spatialcore/annotation/annotate.py +603 -0
- spatialcore/annotation/cellxgene.py +365 -0
- spatialcore/annotation/confidence.py +802 -0
- spatialcore/annotation/discovery.py +529 -0
- spatialcore/annotation/expression.py +363 -0
- spatialcore/annotation/loading.py +529 -0
- spatialcore/annotation/markers.py +297 -0
- spatialcore/annotation/ontology.py +1282 -0
- spatialcore/annotation/patterns.py +247 -0
- spatialcore/annotation/pipeline.py +620 -0
- spatialcore/annotation/synapse.py +380 -0
- spatialcore/annotation/training.py +1457 -0
- spatialcore/annotation/validation.py +422 -0
- spatialcore/core/__init__.py +34 -0
- spatialcore/core/cache.py +118 -0
- spatialcore/core/logging.py +135 -0
- spatialcore/core/metadata.py +149 -0
- spatialcore/core/utils.py +768 -0
- spatialcore/data/gene_mappings/ensembl_to_hugo_human.tsv +86372 -0
- spatialcore/data/markers/canonical_markers.json +83 -0
- spatialcore/data/ontology_mappings/ontology_index.json +63865 -0
- spatialcore/plotting/__init__.py +109 -0
- spatialcore/plotting/benchmark.py +477 -0
- spatialcore/plotting/celltype.py +329 -0
- spatialcore/plotting/confidence.py +413 -0
- spatialcore/plotting/spatial.py +505 -0
- spatialcore/plotting/utils.py +411 -0
- spatialcore/plotting/validation.py +1342 -0
- spatialcore-0.1.9.dist-info/METADATA +213 -0
- spatialcore-0.1.9.dist-info/RECORD +36 -0
- spatialcore-0.1.9.dist-info/WHEEL +5 -0
- spatialcore-0.1.9.dist-info/licenses/LICENSE +201 -0
- 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
|