zmap-tools 0.2.1__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.1 → zmap_tools-0.2.2}/PKG-INFO +2 -2
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/pyproject.toml +2 -2
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/predict/knn_backend.py +93 -15
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/predict/predict.py +131 -35
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/PKG-INFO +2 -2
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/requires.txt +1 -1
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/LICENSE +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/README.md +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/setup.cfg +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/__init__.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/dotplot/__init__.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/dotplot/dotplot_gene.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/dotplot/dotplot_group.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/predict/__init__.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/reference/__init__.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/reference/load_h5ad.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap/reference/markers.py +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/SOURCES.txt +0 -0
- {zmap_tools-0.2.1 → zmap_tools-0.2.2}/src/zmap_tools.egg-info/dependency_links.txt +0 -0
- {zmap_tools-0.2.1 → 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()
|
|
@@ -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
|
|
@@ -2229,6 +2229,17 @@ def aggregate_by_cluster(
|
|
|
2229
2229
|
|
|
2230
2230
|
df = pd.DataFrame(records)
|
|
2231
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
|
+
|
|
2232
2243
|
if save_csv:
|
|
2233
2244
|
os.makedirs(output_dir, exist_ok=True)
|
|
2234
2245
|
out_path = os.path.join(output_dir, f"{label_space}_cluster_summary.csv")
|
|
@@ -2564,6 +2575,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2564
2575
|
# ---- data/keys ----
|
|
2565
2576
|
color_key: str = "ZMAP_Tissue_predicted",
|
|
2566
2577
|
basis: str = "X_umap",
|
|
2578
|
+
ref_basis: str | None = None,
|
|
2567
2579
|
filter_na: bool = True,
|
|
2568
2580
|
|
|
2569
2581
|
# ---- palette handling ----
|
|
@@ -2579,12 +2591,14 @@ def plot_embedding_with_ondata_labels(
|
|
|
2579
2591
|
# ---- figure style ----
|
|
2580
2592
|
figsize: tuple[float, float] = (6, 6),
|
|
2581
2593
|
dpi: int = 200,
|
|
2594
|
+
title_fontsize: float = 10,
|
|
2582
2595
|
|
|
2583
2596
|
# ---- point style ----
|
|
2584
2597
|
ref_size: float = 2,
|
|
2585
2598
|
ref_alpha: float = 0.3,
|
|
2586
2599
|
test_size: float = 2,
|
|
2587
2600
|
test_alpha: float = 1.0,
|
|
2601
|
+
show_ref: bool = True,
|
|
2588
2602
|
cmap: str = "jet",
|
|
2589
2603
|
frameon: bool = False,
|
|
2590
2604
|
sort_order: bool = True,
|
|
@@ -2625,9 +2639,10 @@ def plot_embedding_with_ondata_labels(
|
|
|
2625
2639
|
Plot a query dataset overlaid on the reference embedding, with on-data labels
|
|
2626
2640
|
and an optional vertical time distribution strip.
|
|
2627
2641
|
|
|
2628
|
-
Renders two layers: (1) the full reference embedding
|
|
2629
|
-
background for spatial context
|
|
2630
|
-
|
|
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
|
|
2631
2646
|
``adjustText`` to minimize overlap. A vertical colorbar histogram of
|
|
2632
2647
|
predicted developmental time (``ZMAP_time_id``) can optionally be added
|
|
2633
2648
|
as a strip on the right side of the figure.
|
|
@@ -2642,7 +2657,12 @@ def plot_embedding_with_ondata_labels(
|
|
|
2642
2657
|
Column in ``adata_test.obs`` containing the categorical labels to color
|
|
2643
2658
|
and annotate. Typically a ``_predicted`` column from ``predict_labels_kNN``.
|
|
2644
2659
|
basis : str, default ``"X_umap"``
|
|
2645
|
-
``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"``).
|
|
2646
2666
|
filter_na : bool, default ``True``
|
|
2647
2667
|
Drop query cells with ``NaN`` in ``color_key`` before plotting.
|
|
2648
2668
|
palette : dict or None, default ``None``
|
|
@@ -2674,6 +2694,11 @@ def plot_embedding_with_ondata_labels(
|
|
|
2674
2694
|
Scatter point size for query (projected) cells.
|
|
2675
2695
|
test_alpha : float, default ``1.0``
|
|
2676
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``.
|
|
2677
2702
|
cmap : str, default ``"jet"``
|
|
2678
2703
|
Colormap used for the reference background scatter.
|
|
2679
2704
|
legend_loc : str, default ``"on data"``
|
|
@@ -2730,7 +2755,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2730
2755
|
# Only attempt sync if there's no _color_map dict already available
|
|
2731
2756
|
# (annotate_with_zmap populates _color_map from reference colormaps)
|
|
2732
2757
|
cmap_dict_key = f"{base_obs}_color_map"
|
|
2733
|
-
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:
|
|
2734
2759
|
try:
|
|
2735
2760
|
sync_zmap_colors(adata_ref, obs_key=base_obs)
|
|
2736
2761
|
sync_zmap_colors(adata_test_plot, obs_key=color_key, ref_adata=adata_ref, ref_obs_key=base_obs)
|
|
@@ -2795,6 +2820,9 @@ def plot_embedding_with_ondata_labels(
|
|
|
2795
2820
|
ax_umap = None
|
|
2796
2821
|
ax_strip = None
|
|
2797
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
|
+
|
|
2798
2826
|
# ---- figure + axes layout ----
|
|
2799
2827
|
with plt.rc_context({'figure.figsize': figsize, 'figure.dpi': dpi}):
|
|
2800
2828
|
if has_time:
|
|
@@ -2812,15 +2840,18 @@ def plot_embedding_with_ondata_labels(
|
|
|
2812
2840
|
fig, ax_umap = plt.subplots()
|
|
2813
2841
|
ax_strip = None
|
|
2814
2842
|
|
|
2815
|
-
# ---- reference embedding in UMAP axis ----
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
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
|
|
2824
2855
|
|
|
2825
2856
|
# ---- query overlay in UMAP axis ----
|
|
2826
2857
|
ax = sc.pl.embedding(
|
|
@@ -2840,6 +2871,7 @@ def plot_embedding_with_ondata_labels(
|
|
|
2840
2871
|
palette=palette,
|
|
2841
2872
|
**test_kwargs,
|
|
2842
2873
|
)
|
|
2874
|
+
ax.title.set_fontsize(title_fontsize)
|
|
2843
2875
|
|
|
2844
2876
|
# --- on-data label styling (only when show_labels=True) ---
|
|
2845
2877
|
if show_labels:
|
|
@@ -3117,20 +3149,26 @@ def map_query_labels(
|
|
|
3117
3149
|
fig = None
|
|
3118
3150
|
if show_plot or save_plots:
|
|
3119
3151
|
plt.rcParams["axes.grid"] = False
|
|
3120
|
-
|
|
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))
|
|
3121
3159
|
|
|
3122
3160
|
im = ax.imshow(plot_table.to_numpy(), cmap=cmap, vmin=vmin, vmax=vmax)
|
|
3123
3161
|
|
|
3124
3162
|
ax.set_aspect("equal")
|
|
3125
3163
|
ax.set_xticks(np.arange(plot_table.shape[1]))
|
|
3126
3164
|
ax.set_yticks(np.arange(plot_table.shape[0]))
|
|
3127
|
-
ax.set_xticklabels(plot_table.columns, rotation=90)
|
|
3128
|
-
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)
|
|
3129
3167
|
ax.set_title(title)
|
|
3130
3168
|
ax.set_xlabel(obs_A)
|
|
3131
3169
|
ax.set_ylabel(obs_B)
|
|
3132
3170
|
|
|
3133
|
-
cb = fig.colorbar(im, ax=ax, shrink=
|
|
3171
|
+
cb = fig.colorbar(im, ax=ax, shrink=cb_shrink)
|
|
3134
3172
|
cb.ax.set_ylabel(colorbar_label)
|
|
3135
3173
|
|
|
3136
3174
|
if overlay_values:
|
|
@@ -3354,7 +3392,7 @@ def annotate_with_zmap(
|
|
|
3354
3392
|
- ``.obs[f"{label_space}_predicted"]`` — transferred cell labels.
|
|
3355
3393
|
- ``.obs[f"{label_space}_prob"]`` — label confidence (0–1).
|
|
3356
3394
|
- ``.obs["ZMAP_time_id_predicted"]`` — predicted time (hpf).
|
|
3357
|
-
- ``.obsm["
|
|
3395
|
+
- ``.obsm["X_umap_zmap"]`` — projected UMAP coordinates (if ingested).
|
|
3358
3396
|
- ``.uns['zmap_labels']['_last_space']`` — most recent label_space.
|
|
3359
3397
|
- ``.uns['zmap_labels'][label_space]['_run_config']`` — stored run parameters
|
|
3360
3398
|
for zero-arg on-demand plot accessors.
|
|
@@ -3517,9 +3555,29 @@ def annotate_with_zmap(
|
|
|
3517
3555
|
if do_ingest:
|
|
3518
3556
|
if v >= 1:
|
|
3519
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
|
+
|
|
3520
3564
|
with warnings.catch_warnings():
|
|
3521
3565
|
warnings.simplefilter("ignore", FutureWarning)
|
|
3522
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
|
+
|
|
3523
3581
|
if v >= 1:
|
|
3524
3582
|
_zlog("Ingestion complete.")
|
|
3525
3583
|
|
|
@@ -3593,15 +3651,26 @@ def annotate_with_zmap(
|
|
|
3593
3651
|
adata_query.uns.setdefault("zmap_labels", {})
|
|
3594
3652
|
adata_query.uns["zmap_labels"]["_last_space"] = space
|
|
3595
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
|
+
|
|
3596
3663
|
adata_query.uns["zmap_labels"][space]["_run_config"] = {
|
|
3597
3664
|
"label_space": space,
|
|
3598
3665
|
"ref_label_col": ref_label_col,
|
|
3599
3666
|
"query_label_col": query_label_col,
|
|
3600
3667
|
"time_col": time_col_actual,
|
|
3601
3668
|
"output_dir": space_dir,
|
|
3669
|
+
"basis": _plot_basis,
|
|
3670
|
+
"show_ref": _show_ref,
|
|
3602
3671
|
}
|
|
3603
|
-
# Store reference UMAP for on-demand plot_embedding (
|
|
3604
|
-
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:
|
|
3605
3674
|
adata_query.uns["zmap_labels"][space]["_ref_umap"] = (
|
|
3606
3675
|
np.asarray(adata_ref.obsm["X_umap"]).copy()
|
|
3607
3676
|
)
|
|
@@ -3691,15 +3760,24 @@ def annotate_with_zmap(
|
|
|
3691
3760
|
# ------------------------------------------------------------------
|
|
3692
3761
|
# 9. UMAP overlay figure (v >= 2)
|
|
3693
3762
|
# ------------------------------------------------------------------
|
|
3694
|
-
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:
|
|
3695
3770
|
try:
|
|
3696
3771
|
if v >= 1:
|
|
3697
3772
|
_zlog("Plotting UMAP overlay with predicted labels...")
|
|
3698
3773
|
plot_embedding_with_ondata_labels(
|
|
3699
|
-
adata_ref,
|
|
3774
|
+
adata_ref if _show_ref else None,
|
|
3700
3775
|
adata_query,
|
|
3701
3776
|
color_key=f"{space}_predicted",
|
|
3702
3777
|
time_key=time_col_actual,
|
|
3778
|
+
basis=_plot_basis,
|
|
3779
|
+
ref_basis="X_umap",
|
|
3780
|
+
show_ref=_show_ref,
|
|
3703
3781
|
show=True,
|
|
3704
3782
|
save=save_outputs,
|
|
3705
3783
|
output_dir=space_dir,
|
|
@@ -3714,10 +3792,13 @@ def annotate_with_zmap(
|
|
|
3714
3792
|
# Still save the figure even if not showing it
|
|
3715
3793
|
try:
|
|
3716
3794
|
plot_embedding_with_ondata_labels(
|
|
3717
|
-
adata_ref,
|
|
3795
|
+
adata_ref if _show_ref else None,
|
|
3718
3796
|
adata_query,
|
|
3719
3797
|
color_key=f"{space}_predicted",
|
|
3720
3798
|
time_key=time_col_actual,
|
|
3799
|
+
basis=_plot_basis,
|
|
3800
|
+
ref_basis="X_umap",
|
|
3801
|
+
show_ref=_show_ref,
|
|
3721
3802
|
show=False,
|
|
3722
3803
|
save=True,
|
|
3723
3804
|
output_dir=space_dir,
|
|
@@ -3985,6 +4066,7 @@ def validate_markers(
|
|
|
3985
4066
|
groupby=groupby,
|
|
3986
4067
|
method=method,
|
|
3987
4068
|
corr_method=corr_method,
|
|
4069
|
+
use_raw=False,
|
|
3988
4070
|
)
|
|
3989
4071
|
|
|
3990
4072
|
# ---- Extract and filter DE results per group ----
|
|
@@ -4271,19 +4353,30 @@ def plot_embedding(
|
|
|
4271
4353
|
label_space, config = _resolve_config(adata_query, label_space)
|
|
4272
4354
|
space_store = adata_query.uns.get("zmap_labels", {}).get(label_space, {})
|
|
4273
4355
|
|
|
4274
|
-
|
|
4275
|
-
|
|
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:
|
|
4276
4361
|
raise KeyError(
|
|
4277
|
-
f"No
|
|
4278
|
-
|
|
4279
|
-
"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."
|
|
4280
4364
|
)
|
|
4281
4365
|
|
|
4282
|
-
# Build
|
|
4283
|
-
adata_ref_mini =
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
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
|
|
4287
4380
|
|
|
4288
4381
|
color_key = f"{label_space}_predicted"
|
|
4289
4382
|
time_key = config.get("time_col", "ZMAP_time_id_predicted")
|
|
@@ -4294,6 +4387,9 @@ def plot_embedding(
|
|
|
4294
4387
|
adata_query,
|
|
4295
4388
|
color_key=color_key,
|
|
4296
4389
|
time_key=time_key,
|
|
4390
|
+
basis=plot_basis,
|
|
4391
|
+
ref_basis="X_umap",
|
|
4392
|
+
show_ref=show_ref,
|
|
4297
4393
|
show=True,
|
|
4298
4394
|
save=save,
|
|
4299
4395
|
show_labels=show_labels,
|
|
@@ -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
|