zmap-tools 0.2.0__tar.gz → 0.2.2__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.
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/PKG-INFO +2 -2
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/pyproject.toml +2 -2
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/predict/knn_backend.py +93 -15
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/predict/predict.py +247 -82
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/PKG-INFO +2 -2
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/requires.txt +1 -1
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/LICENSE +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/README.md +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/setup.cfg +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/__init__.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/dotplot/__init__.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/dotplot/dotplot_gene.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/dotplot/dotplot_group.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/predict/__init__.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/reference/__init__.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/reference/load_h5ad.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap/reference/markers.py +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/SOURCES.txt +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/dependency_links.txt +0 -0
- {zmap_tools-0.2.0 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zmap-tools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: API for working with ZMAP (Zebrafish Multi-Atlas Project)
|
|
5
5
|
Author-email: Daniel Wagner <Daniel.Wagner@ucsf.edu>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -50,7 +50,7 @@ License-File: LICENSE
|
|
|
50
50
|
Requires-Dist: anndata>=0.10
|
|
51
51
|
Requires-Dist: scanpy>=1.9
|
|
52
52
|
Requires-Dist: numpy>=1.24
|
|
53
|
-
Requires-Dist: pandas
|
|
53
|
+
Requires-Dist: pandas<3.0,>=2.0
|
|
54
54
|
Requires-Dist: matplotlib>=3.7
|
|
55
55
|
Requires-Dist: requests>=2.31
|
|
56
56
|
Requires-Dist: scipy>=1.10
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "zmap-tools"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "API for working with ZMAP (Zebrafish Multi-Atlas Project)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -30,7 +30,7 @@ dependencies = [
|
|
|
30
30
|
"anndata>=0.10",
|
|
31
31
|
"scanpy>=1.9",
|
|
32
32
|
"numpy>=1.24",
|
|
33
|
-
"pandas>=2.0",
|
|
33
|
+
"pandas>=2.0,<3.0",
|
|
34
34
|
"matplotlib>=3.7",
|
|
35
35
|
"requests>=2.31",
|
|
36
36
|
"scipy>=1.10",
|
|
@@ -18,10 +18,12 @@ _FAISS_INDEX_CACHE_ORDER: list[tuple[str, str, int, int]] = []
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _as_float32_c(x: np.ndarray) -> np.ndarray:
|
|
21
|
+
"""Cast array to float32 and ensure C-contiguous memory layout (required by FAISS)."""
|
|
21
22
|
return np.ascontiguousarray(np.asarray(x, dtype=np.float32))
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _l2_normalize(x: np.ndarray) -> np.ndarray:
|
|
26
|
+
"""Row-wise L2-normalize an array to unit vectors (used for cosine similarity via inner product)."""
|
|
25
27
|
x = _as_float32_c(x)
|
|
26
28
|
norms = np.linalg.norm(x, axis=1, keepdims=True)
|
|
27
29
|
norms = np.where(norms == 0, 1.0, norms)
|
|
@@ -29,6 +31,7 @@ def _l2_normalize(x: np.ndarray) -> np.ndarray:
|
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def _parse_cuda_device(device: str) -> int:
|
|
34
|
+
"""Parse a device string like 'cuda:1' into an integer GPU index. Returns 0 on failure."""
|
|
32
35
|
d = str(device).strip().lower()
|
|
33
36
|
if ":" in d:
|
|
34
37
|
try:
|
|
@@ -44,6 +47,17 @@ def _search_sklearn(
|
|
|
44
47
|
n_neighbors: int,
|
|
45
48
|
metric: str,
|
|
46
49
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
50
|
+
"""
|
|
51
|
+
kNN search using scikit-learn's NearestNeighbors.
|
|
52
|
+
|
|
53
|
+
Used as the fallback when FAISS is unavailable or fails. Slower than FAISS
|
|
54
|
+
on large references but supports any sklearn-compatible metric.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
neighbor_indices : np.ndarray, shape (n_query, n_neighbors)
|
|
59
|
+
distances : np.ndarray, shape (n_query, n_neighbors)
|
|
60
|
+
"""
|
|
47
61
|
nn = NearestNeighbors(n_neighbors=n_neighbors, metric=metric)
|
|
48
62
|
nn.fit(ref)
|
|
49
63
|
distances, neighbor_indices = nn.kneighbors(query, return_distance=True)
|
|
@@ -59,6 +73,44 @@ def _search_faiss(
|
|
|
59
73
|
nprobe: int | None = None,
|
|
60
74
|
cache_key: str | None = None,
|
|
61
75
|
) -> tuple[np.ndarray, np.ndarray, dict[str, Any]]:
|
|
76
|
+
"""
|
|
77
|
+
kNN search using FAISS with optional GPU acceleration and index caching.
|
|
78
|
+
|
|
79
|
+
Builds an IVF-Flat index by default for fast approximate search on large references.
|
|
80
|
+
Falls back to an exact Flat index if IVF training fails. Cosine similarity is
|
|
81
|
+
implemented as inner product after L2-normalization; euclidean uses L2 distance.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
ref : np.ndarray, shape (n_ref, d)
|
|
86
|
+
Reference embedding matrix.
|
|
87
|
+
query : np.ndarray, shape (n_query, d)
|
|
88
|
+
Query embedding matrix.
|
|
89
|
+
n_neighbors : int
|
|
90
|
+
Number of nearest neighbors to return.
|
|
91
|
+
metric : {'euclidean', 'cosine'}
|
|
92
|
+
Distance metric.
|
|
93
|
+
device : str
|
|
94
|
+
Target device — 'cpu', 'auto', 'cuda', or 'cuda:<id>'.
|
|
95
|
+
nprobe : int, optional
|
|
96
|
+
Number of IVF cells to probe during search. Higher values increase recall
|
|
97
|
+
at the cost of speed. Defaults to DEFAULT_FAISS_NPROBE (16).
|
|
98
|
+
cache_key : str, optional
|
|
99
|
+
If provided, the built FAISS index is cached in-memory under this key and
|
|
100
|
+
reused on subsequent calls with matching (key, metric, n_ref, d). Up to
|
|
101
|
+
MAX_FAISS_INDEX_CACHE (16) indices are retained (LRU eviction).
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
indices : np.ndarray, shape (n_query, n_neighbors), dtype int64
|
|
106
|
+
Indices into ref for each neighbor. Entries are -1 (set to NaN in distances)
|
|
107
|
+
when FAISS cannot fill all k neighbors (rare with Flat indices).
|
|
108
|
+
distances : np.ndarray, shape (n_query, n_neighbors), dtype float32
|
|
109
|
+
Euclidean distances or cosine-distance proxies (1 - cosine_similarity).
|
|
110
|
+
meta : dict
|
|
111
|
+
Diagnostic info: backend_used, device_used, index_type, nlist, nprobe,
|
|
112
|
+
cache_key_used, cache_hit.
|
|
113
|
+
"""
|
|
62
114
|
try:
|
|
63
115
|
import faiss # type: ignore
|
|
64
116
|
except Exception as e:
|
|
@@ -233,21 +285,47 @@ def knn_search(
|
|
|
233
285
|
cache_key: str | None = None,
|
|
234
286
|
) -> tuple[np.ndarray, np.ndarray, dict[str, Any]]:
|
|
235
287
|
"""
|
|
236
|
-
Unified kNN search
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
288
|
+
Unified kNN search supporting FAISS and scikit-learn backends.
|
|
289
|
+
|
|
290
|
+
Tries FAISS first (fast, approximate) and falls back to sklearn (exact) if
|
|
291
|
+
FAISS is unavailable or raises. Both backends return consistent output shapes
|
|
292
|
+
and distance semantics.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
ref : np.ndarray, shape (n_ref, d)
|
|
297
|
+
Reference embedding matrix.
|
|
298
|
+
query : np.ndarray, shape (n_query, d)
|
|
299
|
+
Query embedding matrix.
|
|
300
|
+
n_neighbors : int
|
|
301
|
+
Number of nearest neighbors to return per query point.
|
|
302
|
+
metric : {'cosine', 'euclidean'}, default 'cosine'
|
|
303
|
+
Distance metric. Cosine is computed as 1 - cosine_similarity.
|
|
304
|
+
backend : {'auto', 'faiss', 'sklearn'}, default 'auto'
|
|
305
|
+
Search backend. 'auto' tries FAISS first and falls back to sklearn.
|
|
306
|
+
'faiss' also falls back to sklearn with a warning if FAISS fails.
|
|
307
|
+
'sklearn' skips FAISS entirely.
|
|
308
|
+
device : {'auto', 'cpu', 'cuda', 'cuda:<id>'}, default 'auto'
|
|
309
|
+
Device for FAISS index. Ignored by the sklearn backend. 'auto' uses
|
|
310
|
+
GPU if FAISS GPU support is available, otherwise CPU.
|
|
311
|
+
nprobe : int, optional
|
|
312
|
+
Number of IVF partitions to probe during FAISS search. Higher values
|
|
313
|
+
improve recall at the cost of speed. Defaults to DEFAULT_FAISS_NPROBE.
|
|
314
|
+
Ignored by the sklearn backend and FAISS Flat indices.
|
|
315
|
+
cache_key : str, optional
|
|
316
|
+
If provided, the FAISS index built for `ref` is cached in-memory under
|
|
317
|
+
this key and reused on subsequent calls with the same (key, metric,
|
|
318
|
+
n_ref, d). Useful when the same reference is queried repeatedly.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
indices : np.ndarray, shape (n_query, n_neighbors), dtype int64
|
|
323
|
+
Row indices into `ref` for each neighbor, ordered by ascending distance.
|
|
324
|
+
distances : np.ndarray, shape (n_query, n_neighbors), dtype float32
|
|
325
|
+
Corresponding distances. NaN where FAISS returns invalid indices (-1).
|
|
326
|
+
meta : dict
|
|
327
|
+
Diagnostic info including backend_used, device_used, index_type, and
|
|
328
|
+
cache_hit. Useful for verifying which backend and device were actually used.
|
|
251
329
|
"""
|
|
252
330
|
metric = str(metric).lower()
|
|
253
331
|
backend_req = str(backend).lower()
|
|
@@ -962,7 +962,7 @@ def predict_labels_kNN(
|
|
|
962
962
|
)
|
|
963
963
|
|
|
964
964
|
# compact digest to validate cache
|
|
965
|
-
_mask_digest = [int(n_ref_keep), int(np.sum(np.flatnonzero(ref_keep_mask.to_numpy()) % 1048573))]
|
|
965
|
+
_mask_digest = [str(int(n_ref_keep)), str(int(np.sum(np.flatnonzero(ref_keep_mask.to_numpy()) % 1048573)))]
|
|
966
966
|
|
|
967
967
|
# filtered reference arrays
|
|
968
968
|
X_ref_all = adata_ref.obsm[ref_basis]
|
|
@@ -991,7 +991,7 @@ def predict_labels_kNN(
|
|
|
991
991
|
cache.get('query_basis') == query_basis and
|
|
992
992
|
cache.get('metric') == metric and
|
|
993
993
|
cache.get('n_neighbors') == n_neighbors and
|
|
994
|
-
cache.get('mask_digest') == _mask_digest and
|
|
994
|
+
list(cache.get('mask_digest', [])) == list(_mask_digest) and
|
|
995
995
|
cache.get('tissue_mode', 'none') == expected_cache_mode and
|
|
996
996
|
cache.get('knn_backend_requested', 'auto') == knn_backend and
|
|
997
997
|
cache.get('knn_device_requested', 'auto') == knn_device and
|
|
@@ -1101,7 +1101,7 @@ def predict_labels_kNN(
|
|
|
1101
1101
|
|
|
1102
1102
|
predicted_labels = sorted_classes[np.argmax(probabilities_sorted, axis=1)]
|
|
1103
1103
|
predicted_labels = predicted_labels.astype(object)
|
|
1104
|
-
predicted_labels[~has_votes] =
|
|
1104
|
+
predicted_labels[~has_votes] = np.nan
|
|
1105
1105
|
|
|
1106
1106
|
# ---------- outputs ----------
|
|
1107
1107
|
if omit_labels:
|
|
@@ -1224,7 +1224,7 @@ def predict_labels_kNN(
|
|
|
1224
1224
|
adata_query.obs[col_reject] = ~accept
|
|
1225
1225
|
|
|
1226
1226
|
# Mask rejected predictions
|
|
1227
|
-
adata_query.obs.loc[~accept, col_main] =
|
|
1227
|
+
adata_query.obs.loc[~accept, col_main] = np.nan
|
|
1228
1228
|
|
|
1229
1229
|
n_total = len(accept)
|
|
1230
1230
|
n_accept = int(accept.sum())
|
|
@@ -1285,7 +1285,7 @@ def predict_labels_kNN(
|
|
|
1285
1285
|
rare_labels = label_counts[label_counts < min_cells_per_label].index
|
|
1286
1286
|
if len(rare_labels) > 0:
|
|
1287
1287
|
adata_query.obs[col_rareflag] = adata_query.obs[col_main].isin(rare_labels)
|
|
1288
|
-
adata_query.obs.loc[adata_query.obs[col_rareflag], col_main] =
|
|
1288
|
+
adata_query.obs.loc[adata_query.obs[col_rareflag], col_main] = np.nan
|
|
1289
1289
|
adata_query.uns.setdefault('zmap_labels', {}).setdefault(space, {})
|
|
1290
1290
|
adata_query.uns['zmap_labels'][space]['Rare Labels'] = list(rare_labels)
|
|
1291
1291
|
_zlog(f"Filtered {len(rare_labels)} rare labels: {list(rare_labels[:10])}{'...' if len(rare_labels) > 10 else ''}")
|
|
@@ -1671,8 +1671,8 @@ def predict_labels_tissue_kNN(
|
|
|
1671
1671
|
)
|
|
1672
1672
|
|
|
1673
1673
|
mask_digest = [
|
|
1674
|
-
int(n_ref_keep),
|
|
1675
|
-
int(np.sum(np.flatnonzero(ref_keep_mask.to_numpy()) % 1048573)),
|
|
1674
|
+
str(int(n_ref_keep)),
|
|
1675
|
+
str(int(np.sum(np.flatnonzero(ref_keep_mask.to_numpy()) % 1048573))),
|
|
1676
1676
|
]
|
|
1677
1677
|
|
|
1678
1678
|
p_thresh_use = p_thresh
|
|
@@ -2061,6 +2061,8 @@ def summarize_knn_run(adata_query, label_key):
|
|
|
2061
2061
|
("pct_assigned", cov.get("pct_assigned")),
|
|
2062
2062
|
], columns=["Key", "Value"])
|
|
2063
2063
|
|
|
2064
|
+
df["Value"] = df["Value"].astype(str)
|
|
2065
|
+
|
|
2064
2066
|
return df
|
|
2065
2067
|
|
|
2066
2068
|
|
|
@@ -2227,6 +2229,17 @@ def aggregate_by_cluster(
|
|
|
2227
2229
|
|
|
2228
2230
|
df = pd.DataFrame(records)
|
|
2229
2231
|
|
|
2232
|
+
# Ensure object columns are h5ad-serializable in uns:
|
|
2233
|
+
# - numeric columns demoted to object by pd.NA → coerce back to float (pd.NA → np.nan)
|
|
2234
|
+
# - string columns with pd.NA → fillna("") and cast to str
|
|
2235
|
+
_numeric_cols = {"top_fraction", "mean_prob", "margin", "second_fraction"}
|
|
2236
|
+
for col in df.columns:
|
|
2237
|
+
if df[col].dtype == object:
|
|
2238
|
+
if col in _numeric_cols:
|
|
2239
|
+
df[col] = pd.to_numeric(df[col], errors="coerce")
|
|
2240
|
+
else:
|
|
2241
|
+
df[col] = df[col].fillna("").astype(str)
|
|
2242
|
+
|
|
2230
2243
|
if save_csv:
|
|
2231
2244
|
os.makedirs(output_dir, exist_ok=True)
|
|
2232
2245
|
out_path = os.path.join(output_dir, f"{label_space}_cluster_summary.csv")
|
|
@@ -2305,6 +2318,11 @@ def build_cell_annotations_table(
|
|
|
2305
2318
|
df.index.name = "cell_id"
|
|
2306
2319
|
df = df.reset_index()
|
|
2307
2320
|
|
|
2321
|
+
# Ensure object columns are pure str (np.nan in object dtype is not h5ad-serializable in uns)
|
|
2322
|
+
for col in df.columns:
|
|
2323
|
+
if df[col].dtype == object:
|
|
2324
|
+
df[col] = df[col].fillna("").astype(str)
|
|
2325
|
+
|
|
2308
2326
|
if save_csv:
|
|
2309
2327
|
os.makedirs(output_dir, exist_ok=True)
|
|
2310
2328
|
out_path = os.path.join(output_dir, f"{label_space}_cell_annotations.csv")
|
|
@@ -2557,6 +2575,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2557
2575
|
# ---- data/keys ----
|
|
2558
2576
|
color_key: str = "ZMAP_Tissue_predicted",
|
|
2559
2577
|
basis: str = "X_umap",
|
|
2578
|
+
ref_basis: str | None = None,
|
|
2560
2579
|
filter_na: bool = True,
|
|
2561
2580
|
|
|
2562
2581
|
# ---- palette handling ----
|
|
@@ -2572,10 +2591,14 @@ def plot_embedding_with_ondata_labels(
|
|
|
2572
2591
|
# ---- figure style ----
|
|
2573
2592
|
figsize: tuple[float, float] = (6, 6),
|
|
2574
2593
|
dpi: int = 200,
|
|
2594
|
+
title_fontsize: float = 10,
|
|
2575
2595
|
|
|
2576
|
-
# ---- point
|
|
2596
|
+
# ---- point style ----
|
|
2577
2597
|
ref_size: float = 2,
|
|
2598
|
+
ref_alpha: float = 0.3,
|
|
2578
2599
|
test_size: float = 2,
|
|
2600
|
+
test_alpha: float = 1.0,
|
|
2601
|
+
show_ref: bool = True,
|
|
2579
2602
|
cmap: str = "jet",
|
|
2580
2603
|
frameon: bool = False,
|
|
2581
2604
|
sort_order: bool = True,
|
|
@@ -2583,6 +2606,9 @@ def plot_embedding_with_ondata_labels(
|
|
|
2583
2606
|
legend_fontsize: float = 5,
|
|
2584
2607
|
legend_fontweight: str = "normal",
|
|
2585
2608
|
|
|
2609
|
+
# ---- label visibility ----
|
|
2610
|
+
show_labels: bool = True,
|
|
2611
|
+
|
|
2586
2612
|
# ---- label styling ----
|
|
2587
2613
|
recolor_labels_from_palette: bool = True,
|
|
2588
2614
|
text_stroke_width: float = 1.0,
|
|
@@ -2613,9 +2639,10 @@ def plot_embedding_with_ondata_labels(
|
|
|
2613
2639
|
Plot a query dataset overlaid on the reference embedding, with on-data labels
|
|
2614
2640
|
and an optional vertical time distribution strip.
|
|
2615
2641
|
|
|
2616
|
-
Renders two layers: (1) the full reference embedding
|
|
2617
|
-
background for spatial context
|
|
2618
|
-
|
|
2642
|
+
Renders up to two layers: (1) optionally, the full reference embedding
|
|
2643
|
+
as a faint grey background for spatial context (controlled by
|
|
2644
|
+
``show_ref``), and (2) the query cells colored by a predicted label
|
|
2645
|
+
column. Labels are drawn directly on the embedding using
|
|
2619
2646
|
``adjustText`` to minimize overlap. A vertical colorbar histogram of
|
|
2620
2647
|
predicted developmental time (``ZMAP_time_id``) can optionally be added
|
|
2621
2648
|
as a strip on the right side of the figure.
|
|
@@ -2630,7 +2657,12 @@ def plot_embedding_with_ondata_labels(
|
|
|
2630
2657
|
Column in ``adata_test.obs`` containing the categorical labels to color
|
|
2631
2658
|
and annotate. Typically a ``_predicted`` column from ``predict_labels_kNN``.
|
|
2632
2659
|
basis : str, default ``"X_umap"``
|
|
2633
|
-
``obsm`` key used for the 2D embedding coordinates in
|
|
2660
|
+
``obsm`` key used for the 2D embedding coordinates in the query dataset.
|
|
2661
|
+
ref_basis : str or None, default ``None``
|
|
2662
|
+
``obsm`` key for the reference background embedding. When ``None``,
|
|
2663
|
+
falls back to ``basis``. Useful when the query embedding lives in a
|
|
2664
|
+
different key than the reference (e.g. ``basis="X_umap_zmap"`` for
|
|
2665
|
+
the query while the reference uses ``"X_umap"``).
|
|
2634
2666
|
filter_na : bool, default ``True``
|
|
2635
2667
|
Drop query cells with ``NaN`` in ``color_key`` before plotting.
|
|
2636
2668
|
palette : dict or None, default ``None``
|
|
@@ -2653,15 +2685,34 @@ def plot_embedding_with_ondata_labels(
|
|
|
2653
2685
|
Figure size in inches ``(width, height)``.
|
|
2654
2686
|
dpi : int, default ``200``
|
|
2655
2687
|
Figure resolution.
|
|
2656
|
-
ref_size
|
|
2657
|
-
Scatter point
|
|
2688
|
+
ref_size : float, default ``2``
|
|
2689
|
+
Scatter point size for reference background cells.
|
|
2690
|
+
ref_alpha : float, default ``0.3``
|
|
2691
|
+
Opacity of reference background points. Lower values push the
|
|
2692
|
+
reference further into the background.
|
|
2693
|
+
test_size : float, default ``2``
|
|
2694
|
+
Scatter point size for query (projected) cells.
|
|
2695
|
+
test_alpha : float, default ``1.0``
|
|
2696
|
+
Opacity of query overlay points.
|
|
2697
|
+
show_ref : bool, default ``True``
|
|
2698
|
+
If ``True``, draw the reference embedding as a faint grey background
|
|
2699
|
+
for spatial context. If ``False``, only the query cells are plotted
|
|
2700
|
+
(useful when ``do_ingest=False``). When ``False``, ``adata_ref``
|
|
2701
|
+
may be ``None``.
|
|
2658
2702
|
cmap : str, default ``"jet"``
|
|
2659
2703
|
Colormap used for the reference background scatter.
|
|
2660
2704
|
legend_loc : str, default ``"on data"``
|
|
2661
2705
|
Where to place the category legend. ``"on data"`` draws labels directly
|
|
2662
2706
|
at centroid positions; other values follow matplotlib legend conventions.
|
|
2707
|
+
Ignored when ``show_labels=False`` (forced to ``"none"``).
|
|
2663
2708
|
legend_fontsize, legend_fontweight : float and str, default ``5`` and ``"normal"``
|
|
2664
2709
|
Font size and weight for on-data legend labels.
|
|
2710
|
+
show_labels : bool, default ``True``
|
|
2711
|
+
If ``True``, draw on-data text labels at category centroids with
|
|
2712
|
+
``adjustText`` repositioning and optional arrow connectors. If
|
|
2713
|
+
``False``, suppress all text labels and arrows — only the colored
|
|
2714
|
+
scatter is shown, which is useful for clean figures or when the
|
|
2715
|
+
number of categories is too large for readable labels.
|
|
2665
2716
|
replace_underscores : bool, default ``True``
|
|
2666
2717
|
Replace underscores in label strings with line breaks for cleaner
|
|
2667
2718
|
on-data annotation.
|
|
@@ -2669,17 +2720,23 @@ def plot_embedding_with_ondata_labels(
|
|
|
2669
2720
|
``(x_expand, y_expand)`` passed to ``adjustText`` for label placement.
|
|
2670
2721
|
match_arrow_color_to_text : bool, default ``True``
|
|
2671
2722
|
Color annotation arrows to match their corresponding text label.
|
|
2723
|
+
ref_kwargs : dict or None, default ``None``
|
|
2724
|
+
Extra keyword arguments forwarded to the reference ``sc.pl.embedding``
|
|
2725
|
+
call. Explicit ``ref_alpha`` takes priority over ``alpha`` in this dict.
|
|
2726
|
+
test_kwargs : dict or None, default ``None``
|
|
2727
|
+
Extra keyword arguments forwarded to the query ``sc.pl.embedding``
|
|
2728
|
+
call. Explicit ``test_alpha`` takes priority over ``alpha`` in this dict.
|
|
2672
2729
|
show : bool, default ``False``
|
|
2673
2730
|
Call ``plt.show()`` after rendering.
|
|
2674
2731
|
save : bool, default ``True``
|
|
2675
|
-
Save the figure as PNG and PDF to
|
|
2732
|
+
Save the figure as PNG and PDF to ``output_dir``.
|
|
2676
2733
|
return_ax : bool, default ``False``
|
|
2677
2734
|
Return the main ``matplotlib.axes.Axes`` object.
|
|
2678
2735
|
|
|
2679
2736
|
Returns
|
|
2680
2737
|
-------
|
|
2681
|
-
|
|
2682
|
-
|
|
2738
|
+
tuple or None
|
|
2739
|
+
``(fig, ax_umap, ax_strip)`` when ``return_ax=True``, otherwise ``None``.
|
|
2683
2740
|
"""
|
|
2684
2741
|
# ---- prepare test AnnData (drop NAs on requested key, cast to categorical) ----
|
|
2685
2742
|
if filter_na:
|
|
@@ -2698,7 +2755,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2698
2755
|
# Only attempt sync if there's no _color_map dict already available
|
|
2699
2756
|
# (annotate_with_zmap populates _color_map from reference colormaps)
|
|
2700
2757
|
cmap_dict_key = f"{base_obs}_color_map"
|
|
2701
|
-
if cmap_dict_key not in adata_test_plot.uns:
|
|
2758
|
+
if cmap_dict_key not in adata_test_plot.uns and adata_ref is not None:
|
|
2702
2759
|
try:
|
|
2703
2760
|
sync_zmap_colors(adata_ref, obs_key=base_obs)
|
|
2704
2761
|
sync_zmap_colors(adata_test_plot, obs_key=color_key, ref_adata=adata_ref, ref_obs_key=base_obs)
|
|
@@ -2745,6 +2802,13 @@ def plot_embedding_with_ondata_labels(
|
|
|
2745
2802
|
test_kwargs = {} if test_kwargs is None else dict(test_kwargs)
|
|
2746
2803
|
time_strip_kwargs = {} if time_strip_kwargs is None else dict(time_strip_kwargs)
|
|
2747
2804
|
|
|
2805
|
+
# ---- Inject alpha into kwargs (explicit params take priority) ----
|
|
2806
|
+
ref_kwargs.setdefault("alpha", ref_alpha)
|
|
2807
|
+
test_kwargs.setdefault("alpha", test_alpha)
|
|
2808
|
+
|
|
2809
|
+
# ---- Resolve legend_loc: suppress labels when show_labels=False ----
|
|
2810
|
+
legend_loc_use = legend_loc if show_labels else "none"
|
|
2811
|
+
|
|
2748
2812
|
# ---- Check if we actually have a time vector ----
|
|
2749
2813
|
has_time = (
|
|
2750
2814
|
show_time_strip and
|
|
@@ -2756,6 +2820,9 @@ def plot_embedding_with_ondata_labels(
|
|
|
2756
2820
|
ax_umap = None
|
|
2757
2821
|
ax_strip = None
|
|
2758
2822
|
|
|
2823
|
+
# ---- Resolve ref_basis: defaults to basis if not provided ----
|
|
2824
|
+
ref_basis_use = ref_basis if ref_basis is not None else basis
|
|
2825
|
+
|
|
2759
2826
|
# ---- figure + axes layout ----
|
|
2760
2827
|
with plt.rc_context({'figure.figsize': figsize, 'figure.dpi': dpi}):
|
|
2761
2828
|
if has_time:
|
|
@@ -2773,15 +2840,18 @@ def plot_embedding_with_ondata_labels(
|
|
|
2773
2840
|
fig, ax_umap = plt.subplots()
|
|
2774
2841
|
ax_strip = None
|
|
2775
2842
|
|
|
2776
|
-
# ---- reference embedding in UMAP axis ----
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2843
|
+
# ---- reference embedding in UMAP axis (optional) ----
|
|
2844
|
+
if show_ref:
|
|
2845
|
+
ax = sc.pl.embedding(
|
|
2846
|
+
adata_ref,
|
|
2847
|
+
basis=ref_basis_use,
|
|
2848
|
+
show=False,
|
|
2849
|
+
s=ref_size,
|
|
2850
|
+
ax=ax_umap,
|
|
2851
|
+
**ref_kwargs,
|
|
2852
|
+
)
|
|
2853
|
+
else:
|
|
2854
|
+
ax = ax_umap
|
|
2785
2855
|
|
|
2786
2856
|
# ---- query overlay in UMAP axis ----
|
|
2787
2857
|
ax = sc.pl.embedding(
|
|
@@ -2793,7 +2863,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2793
2863
|
basis=basis,
|
|
2794
2864
|
sort_order=sort_order,
|
|
2795
2865
|
size=test_size,
|
|
2796
|
-
legend_loc=
|
|
2866
|
+
legend_loc=legend_loc_use,
|
|
2797
2867
|
legend_fontweight=legend_fontweight,
|
|
2798
2868
|
ax=ax,
|
|
2799
2869
|
show=False,
|
|
@@ -2801,42 +2871,44 @@ def plot_embedding_with_ondata_labels(
|
|
|
2801
2871
|
palette=palette,
|
|
2802
2872
|
**test_kwargs,
|
|
2803
2873
|
)
|
|
2874
|
+
ax.title.set_fontsize(title_fontsize)
|
|
2804
2875
|
|
|
2805
|
-
# ---
|
|
2806
|
-
|
|
2876
|
+
# --- on-data label styling (only when show_labels=True) ---
|
|
2877
|
+
if show_labels:
|
|
2878
|
+
texts = [t for t in ax.texts]
|
|
2807
2879
|
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
# --- add white border to text labels ---
|
|
2816
|
-
for t in texts:
|
|
2817
|
-
t.set_path_effects([pe.withStroke(linewidth=text_stroke_width, foreground='white')])
|
|
2880
|
+
# --- recolor existing on-data text labels by palette ---
|
|
2881
|
+
if recolor_labels_from_palette:
|
|
2882
|
+
for t in texts:
|
|
2883
|
+
label = t.get_text()
|
|
2884
|
+
if label in palette:
|
|
2885
|
+
t.set_color(palette[label])
|
|
2818
2886
|
|
|
2819
|
-
|
|
2820
|
-
if replace_underscores:
|
|
2887
|
+
# --- add white border to text labels ---
|
|
2821
2888
|
for t in texts:
|
|
2822
|
-
t.
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2889
|
+
t.set_path_effects([pe.withStroke(linewidth=text_stroke_width, foreground='white')])
|
|
2890
|
+
|
|
2891
|
+
# --- replace underscores with line breaks ---
|
|
2892
|
+
if replace_underscores:
|
|
2893
|
+
for t in texts:
|
|
2894
|
+
t.set_text(t.get_text().replace(linebreak_from, linebreak_to))
|
|
2895
|
+
|
|
2896
|
+
# --- adjust label positions with slim arrows ---
|
|
2897
|
+
if len(texts) > 0:
|
|
2898
|
+
adjust_text(
|
|
2899
|
+
texts,
|
|
2900
|
+
ax=ax,
|
|
2901
|
+
expand=adjust_expand,
|
|
2902
|
+
arrowprops=arrowprops,
|
|
2903
|
+
min_arrow_len=min_arrow_len,
|
|
2904
|
+
)
|
|
2833
2905
|
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2906
|
+
# --- match arrow color to label color ---
|
|
2907
|
+
if match_arrow_color_to_text and len(texts) > 0:
|
|
2908
|
+
arrows = [p for p in ax.patches if isinstance(p, FancyArrowPatch)]
|
|
2909
|
+
for t, a in zip(texts, arrows[-len(texts):]):
|
|
2910
|
+
a.set_color(t.get_color())
|
|
2911
|
+
a.set_alpha(arrow_alpha)
|
|
2840
2912
|
|
|
2841
2913
|
# ---- time distribution strip on the right (rotated) ----
|
|
2842
2914
|
if has_time and ax_strip is not None:
|
|
@@ -2920,7 +2992,6 @@ def plot_embedding_with_ondata_labels(
|
|
|
2920
2992
|
return None
|
|
2921
2993
|
|
|
2922
2994
|
|
|
2923
|
-
|
|
2924
2995
|
# ================================================================
|
|
2925
2996
|
# 6. Overlap matrix & plot
|
|
2926
2997
|
# ================================================================
|
|
@@ -3078,20 +3149,26 @@ def map_query_labels(
|
|
|
3078
3149
|
fig = None
|
|
3079
3150
|
if show_plot or save_plots:
|
|
3080
3151
|
plt.rcParams["axes.grid"] = False
|
|
3081
|
-
|
|
3152
|
+
n_rows, n_cols = plot_table.shape
|
|
3153
|
+
tick_fontsize = 8
|
|
3154
|
+
cell_size = 0.25 # inches per cell — fits 8pt labels without crowding
|
|
3155
|
+
fig_w = min(28, max(figsize, n_cols * cell_size))
|
|
3156
|
+
fig_h = min(28, max(figsize, n_rows * cell_size))
|
|
3157
|
+
cb_shrink = min(0.8, 3.0 / fig_h) # keep colorbar ~3 inches tall
|
|
3158
|
+
fig, ax = plt.subplots(figsize=(fig_w, fig_h))
|
|
3082
3159
|
|
|
3083
3160
|
im = ax.imshow(plot_table.to_numpy(), cmap=cmap, vmin=vmin, vmax=vmax)
|
|
3084
3161
|
|
|
3085
3162
|
ax.set_aspect("equal")
|
|
3086
3163
|
ax.set_xticks(np.arange(plot_table.shape[1]))
|
|
3087
3164
|
ax.set_yticks(np.arange(plot_table.shape[0]))
|
|
3088
|
-
ax.set_xticklabels(plot_table.columns, rotation=90)
|
|
3089
|
-
ax.set_yticklabels(plot_table.index)
|
|
3165
|
+
ax.set_xticklabels(plot_table.columns, rotation=90, fontsize=tick_fontsize)
|
|
3166
|
+
ax.set_yticklabels(plot_table.index, fontsize=tick_fontsize)
|
|
3090
3167
|
ax.set_title(title)
|
|
3091
3168
|
ax.set_xlabel(obs_A)
|
|
3092
3169
|
ax.set_ylabel(obs_B)
|
|
3093
3170
|
|
|
3094
|
-
cb = fig.colorbar(im, ax=ax, shrink=
|
|
3171
|
+
cb = fig.colorbar(im, ax=ax, shrink=cb_shrink)
|
|
3095
3172
|
cb.ax.set_ylabel(colorbar_label)
|
|
3096
3173
|
|
|
3097
3174
|
if overlay_values:
|
|
@@ -3315,7 +3392,7 @@ def annotate_with_zmap(
|
|
|
3315
3392
|
- ``.obs[f"{label_space}_predicted"]`` — transferred cell labels.
|
|
3316
3393
|
- ``.obs[f"{label_space}_prob"]`` — label confidence (0–1).
|
|
3317
3394
|
- ``.obs["ZMAP_time_id_predicted"]`` — predicted time (hpf).
|
|
3318
|
-
- ``.obsm["
|
|
3395
|
+
- ``.obsm["X_umap_zmap"]`` — projected UMAP coordinates (if ingested).
|
|
3319
3396
|
- ``.uns['zmap_labels']['_last_space']`` — most recent label_space.
|
|
3320
3397
|
- ``.uns['zmap_labels'][label_space]['_run_config']`` — stored run parameters
|
|
3321
3398
|
for zero-arg on-demand plot accessors.
|
|
@@ -3478,9 +3555,29 @@ def annotate_with_zmap(
|
|
|
3478
3555
|
if do_ingest:
|
|
3479
3556
|
if v >= 1:
|
|
3480
3557
|
_zlog("Ingesting query into reference UMAP...")
|
|
3558
|
+
|
|
3559
|
+
# Stash pre-existing X_umap so symphony doesn't clobber it
|
|
3560
|
+
_had_existing_umap = "X_umap" in adata_query.obsm
|
|
3561
|
+
if _had_existing_umap:
|
|
3562
|
+
_existing_umap = adata_query.obsm["X_umap"].copy()
|
|
3563
|
+
|
|
3481
3564
|
with warnings.catch_warnings():
|
|
3482
3565
|
warnings.simplefilter("ignore", FutureWarning)
|
|
3483
3566
|
sp.tl.ingest(adata_query=adata_query, adata_ref=adata_ref)
|
|
3567
|
+
|
|
3568
|
+
# Move ingested coords to X_umap_zmap (non-destructive)
|
|
3569
|
+
adata_query.obsm["X_umap_zmap"] = adata_query.obsm["X_umap"].copy()
|
|
3570
|
+
|
|
3571
|
+
# Restore original X_umap if one existed, otherwise remove
|
|
3572
|
+
if _had_existing_umap:
|
|
3573
|
+
adata_query.obsm["X_umap"] = _existing_umap
|
|
3574
|
+
if v >= 1:
|
|
3575
|
+
_zlog("Restored pre-existing X_umap; ingested coords in X_umap_zmap.")
|
|
3576
|
+
else:
|
|
3577
|
+
del adata_query.obsm["X_umap"]
|
|
3578
|
+
if v >= 1:
|
|
3579
|
+
_zlog("Ingested coords stored in X_umap_zmap.")
|
|
3580
|
+
|
|
3484
3581
|
if v >= 1:
|
|
3485
3582
|
_zlog("Ingestion complete.")
|
|
3486
3583
|
|
|
@@ -3554,15 +3651,26 @@ def annotate_with_zmap(
|
|
|
3554
3651
|
adata_query.uns.setdefault("zmap_labels", {})
|
|
3555
3652
|
adata_query.uns["zmap_labels"]["_last_space"] = space
|
|
3556
3653
|
adata_query.uns["zmap_labels"].setdefault(space, {})
|
|
3654
|
+
# Determine embedding basis and whether to show reference background
|
|
3655
|
+
if do_ingest:
|
|
3656
|
+
_plot_basis = "X_umap_zmap"
|
|
3657
|
+
_show_ref = True
|
|
3658
|
+
else:
|
|
3659
|
+
# No ingest → query lives in its own UMAP space
|
|
3660
|
+
_plot_basis = "X_umap" if "X_umap" in adata_query.obsm else None
|
|
3661
|
+
_show_ref = False
|
|
3662
|
+
|
|
3557
3663
|
adata_query.uns["zmap_labels"][space]["_run_config"] = {
|
|
3558
3664
|
"label_space": space,
|
|
3559
3665
|
"ref_label_col": ref_label_col,
|
|
3560
3666
|
"query_label_col": query_label_col,
|
|
3561
3667
|
"time_col": time_col_actual,
|
|
3562
3668
|
"output_dir": space_dir,
|
|
3669
|
+
"basis": _plot_basis,
|
|
3670
|
+
"show_ref": _show_ref,
|
|
3563
3671
|
}
|
|
3564
|
-
# Store reference UMAP for on-demand plot_embedding (
|
|
3565
|
-
if "X_umap" in adata_ref.obsm:
|
|
3672
|
+
# Store reference UMAP for on-demand plot_embedding (only when ingested)
|
|
3673
|
+
if do_ingest and "X_umap" in adata_ref.obsm:
|
|
3566
3674
|
adata_query.uns["zmap_labels"][space]["_ref_umap"] = (
|
|
3567
3675
|
np.asarray(adata_ref.obsm["X_umap"]).copy()
|
|
3568
3676
|
)
|
|
@@ -3652,15 +3760,24 @@ def annotate_with_zmap(
|
|
|
3652
3760
|
# ------------------------------------------------------------------
|
|
3653
3761
|
# 9. UMAP overlay figure (v >= 2)
|
|
3654
3762
|
# ------------------------------------------------------------------
|
|
3655
|
-
if
|
|
3763
|
+
if _plot_basis is None:
|
|
3764
|
+
if v >= 1:
|
|
3765
|
+
_zlog(
|
|
3766
|
+
"Skipping UMAP overlay: no embedding found "
|
|
3767
|
+
"(do_ingest=False and no pre-existing X_umap)."
|
|
3768
|
+
)
|
|
3769
|
+
elif v >= 2:
|
|
3656
3770
|
try:
|
|
3657
3771
|
if v >= 1:
|
|
3658
3772
|
_zlog("Plotting UMAP overlay with predicted labels...")
|
|
3659
3773
|
plot_embedding_with_ondata_labels(
|
|
3660
|
-
adata_ref,
|
|
3774
|
+
adata_ref if _show_ref else None,
|
|
3661
3775
|
adata_query,
|
|
3662
3776
|
color_key=f"{space}_predicted",
|
|
3663
3777
|
time_key=time_col_actual,
|
|
3778
|
+
basis=_plot_basis,
|
|
3779
|
+
ref_basis="X_umap",
|
|
3780
|
+
show_ref=_show_ref,
|
|
3664
3781
|
show=True,
|
|
3665
3782
|
save=save_outputs,
|
|
3666
3783
|
output_dir=space_dir,
|
|
@@ -3675,10 +3792,13 @@ def annotate_with_zmap(
|
|
|
3675
3792
|
# Still save the figure even if not showing it
|
|
3676
3793
|
try:
|
|
3677
3794
|
plot_embedding_with_ondata_labels(
|
|
3678
|
-
adata_ref,
|
|
3795
|
+
adata_ref if _show_ref else None,
|
|
3679
3796
|
adata_query,
|
|
3680
3797
|
color_key=f"{space}_predicted",
|
|
3681
3798
|
time_key=time_col_actual,
|
|
3799
|
+
basis=_plot_basis,
|
|
3800
|
+
ref_basis="X_umap",
|
|
3801
|
+
show_ref=_show_ref,
|
|
3682
3802
|
show=False,
|
|
3683
3803
|
save=True,
|
|
3684
3804
|
output_dir=space_dir,
|
|
@@ -3946,6 +4066,7 @@ def validate_markers(
|
|
|
3946
4066
|
groupby=groupby,
|
|
3947
4067
|
method=method,
|
|
3948
4068
|
corr_method=corr_method,
|
|
4069
|
+
use_raw=False,
|
|
3949
4070
|
)
|
|
3950
4071
|
|
|
3951
4072
|
# ---- Extract and filter DE results per group ----
|
|
@@ -4186,8 +4307,14 @@ def plot_embedding(
|
|
|
4186
4307
|
label_space: str | None = None,
|
|
4187
4308
|
*,
|
|
4188
4309
|
save: bool = False,
|
|
4310
|
+
show_labels: bool = True,
|
|
4311
|
+
ref_size: float = 2,
|
|
4312
|
+
ref_alpha: float = 0.3,
|
|
4313
|
+
test_size: float = 2,
|
|
4314
|
+
test_alpha: float = 1.0,
|
|
4315
|
+
return_ax: bool = False,
|
|
4189
4316
|
**kwargs,
|
|
4190
|
-
) -> None:
|
|
4317
|
+
) -> tuple | None:
|
|
4191
4318
|
"""
|
|
4192
4319
|
Re-plot the UMAP overlay with on-data labels and time strip.
|
|
4193
4320
|
|
|
@@ -4202,37 +4329,75 @@ def plot_embedding(
|
|
|
4202
4329
|
Label namespace. When ``None``, uses the most recent run.
|
|
4203
4330
|
save : bool, default ``False``
|
|
4204
4331
|
Save the figure to the run's output directory.
|
|
4332
|
+
show_labels : bool, default ``True``
|
|
4333
|
+
If ``True``, draw on-data text labels at category centroids.
|
|
4334
|
+
If ``False``, suppress all text labels and arrows.
|
|
4335
|
+
ref_size : float, default ``2``
|
|
4336
|
+
Scatter point size for reference background cells.
|
|
4337
|
+
ref_alpha : float, default ``0.3``
|
|
4338
|
+
Opacity of reference background points.
|
|
4339
|
+
test_size : float, default ``2``
|
|
4340
|
+
Scatter point size for query (projected) cells.
|
|
4341
|
+
test_alpha : float, default ``1.0``
|
|
4342
|
+
Opacity of query overlay points.
|
|
4343
|
+
return_ax : bool, default ``False``
|
|
4344
|
+
Return ``(fig, ax_umap, ax_strip)`` instead of ``None``.
|
|
4205
4345
|
**kwargs
|
|
4206
4346
|
Extra keyword arguments forwarded to ``plot_embedding_with_ondata_labels``.
|
|
4347
|
+
|
|
4348
|
+
Returns
|
|
4349
|
+
-------
|
|
4350
|
+
tuple or None
|
|
4351
|
+
``(fig, ax_umap, ax_strip)`` when ``return_ax=True``, otherwise ``None``.
|
|
4207
4352
|
"""
|
|
4208
4353
|
label_space, config = _resolve_config(adata_query, label_space)
|
|
4209
4354
|
space_store = adata_query.uns.get("zmap_labels", {}).get(label_space, {})
|
|
4210
4355
|
|
|
4211
|
-
|
|
4212
|
-
|
|
4356
|
+
# Resolve basis and show_ref from stored config
|
|
4357
|
+
plot_basis = config.get("basis", "X_umap_zmap")
|
|
4358
|
+
show_ref = config.get("show_ref", True)
|
|
4359
|
+
|
|
4360
|
+
if plot_basis is None:
|
|
4213
4361
|
raise KeyError(
|
|
4214
|
-
f"No
|
|
4215
|
-
|
|
4216
|
-
"Run annotate_with_zmap first."
|
|
4362
|
+
f"No embedding basis stored for label_space='{label_space}'. "
|
|
4363
|
+
"The original run had do_ingest=False and no pre-existing X_umap."
|
|
4217
4364
|
)
|
|
4218
4365
|
|
|
4219
|
-
# Build
|
|
4220
|
-
adata_ref_mini =
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4366
|
+
# Build reference shell only when needed
|
|
4367
|
+
adata_ref_mini = None
|
|
4368
|
+
if show_ref:
|
|
4369
|
+
ref_umap = space_store.get("_ref_umap")
|
|
4370
|
+
if ref_umap is None:
|
|
4371
|
+
raise KeyError(
|
|
4372
|
+
f"No stored reference UMAP found at "
|
|
4373
|
+
f"adata.uns['zmap_labels']['{label_space}']['_ref_umap']. "
|
|
4374
|
+
"Run annotate_with_zmap first."
|
|
4375
|
+
)
|
|
4376
|
+
adata_ref_mini = ad.AnnData(
|
|
4377
|
+
obs=pd.DataFrame(index=[f"ref_{i}" for i in range(ref_umap.shape[0])]),
|
|
4378
|
+
)
|
|
4379
|
+
adata_ref_mini.obsm["X_umap"] = ref_umap
|
|
4224
4380
|
|
|
4225
4381
|
color_key = f"{label_space}_predicted"
|
|
4226
4382
|
time_key = config.get("time_col", "ZMAP_time_id_predicted")
|
|
4227
4383
|
space_dir = config.get("output_dir", os.path.join("zmap_predict", label_space))
|
|
4228
4384
|
|
|
4229
|
-
plot_embedding_with_ondata_labels(
|
|
4385
|
+
return plot_embedding_with_ondata_labels(
|
|
4230
4386
|
adata_ref_mini,
|
|
4231
4387
|
adata_query,
|
|
4232
4388
|
color_key=color_key,
|
|
4233
4389
|
time_key=time_key,
|
|
4390
|
+
basis=plot_basis,
|
|
4391
|
+
ref_basis="X_umap",
|
|
4392
|
+
show_ref=show_ref,
|
|
4234
4393
|
show=True,
|
|
4235
4394
|
save=save,
|
|
4395
|
+
show_labels=show_labels,
|
|
4396
|
+
ref_size=ref_size,
|
|
4397
|
+
ref_alpha=ref_alpha,
|
|
4398
|
+
test_size=test_size,
|
|
4399
|
+
test_alpha=test_alpha,
|
|
4400
|
+
return_ax=return_ax,
|
|
4236
4401
|
output_dir=space_dir,
|
|
4237
4402
|
**kwargs,
|
|
4238
4403
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zmap-tools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: API for working with ZMAP (Zebrafish Multi-Atlas Project)
|
|
5
5
|
Author-email: Daniel Wagner <Daniel.Wagner@ucsf.edu>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -50,7 +50,7 @@ License-File: LICENSE
|
|
|
50
50
|
Requires-Dist: anndata>=0.10
|
|
51
51
|
Requires-Dist: scanpy>=1.9
|
|
52
52
|
Requires-Dist: numpy>=1.24
|
|
53
|
-
Requires-Dist: pandas
|
|
53
|
+
Requires-Dist: pandas<3.0,>=2.0
|
|
54
54
|
Requires-Dist: matplotlib>=3.7
|
|
55
55
|
Requires-Dist: requests>=2.31
|
|
56
56
|
Requires-Dist: scipy>=1.10
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|