Single-cell plotting helpers for scanpy and anndata workflows
Project description
scuva
Single Cell Utility for Visualization and Analysis
scuva is a plotting helper library for scanpy and anndata workflows. It focuses on the repetitive parts of single-cell figure-making: consistent UMAP styling, categorical color management, shared legends/colorbars, and compact composition plots.
What it does
scuva currently provides three main groups of helpers:
| Area | Functions | Purpose |
|---|---|---|
| UMAP plotting | umap, multiple_umap, umap_split |
Plot categorical or continuous features from an AnnData object with consistent legends and colorbars. |
| Composition plotting | graph_counts, graph_proportions |
Summarize cell counts or percentages across observation columns. |
| Text and color utilities | set_categorical_colors, get_categorical_colormap, make_legend, make_colorbar, rename, clean_title, wrap_join |
Keep labels, legends, and category colors readable and consistent. |
Installation
pip install scuva
The package metadata currently lists these runtime dependencies:
anndatamatplotlibnumpypandasscanpy
Expected AnnData conventions
scuva relies on the AnnData object to follow scanpy conventions:
- UMAP coordinates live in
adata.obsm["X_umap"]unless you pass a differentumap_obsm_key. - Categorical plotting functions expect the relevant
adata.obscolumn to use a pandas categorical dtype. - Category colors are stored in the
adata.uns[f"{feature}_colors"]entry. - Optional display renaming can be stored in
adata.uns["rename_dict"]. graph_proportions,set_categorical_colors, and most categorical legend helpers expect the relevant observation columns to be pandas categoricals.graph_countsalso expectshueto be categorical because colors are read fromadata.obs[hue].cat.categories.get_expression_by_obsforwards itslayerargument toscanpy.tl.rank_genes_groupsand uses the same matrix again when attaching summary columns.
If a helper depends on one of these conventions, the function usually raises a ValueError with a direct explanation when the input does not match.
Quick start
import scanpy as sc
import scuva as scv
adata = sc.read_10x_mtx("test/data/pbmc3k/hg19")
adata.layers["counts"] = adata.X.copy()
sc.pp.normalize_total(adata, target_sum=10_000)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(
adata,
flavor="seurat_v3",
n_top_genes=2000,
layer="counts",
)
sc.tl.pca(adata, svd_solver="arpack", use_highly_variable=True)
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=8, use_rep="X_pca")
sc.tl.umap(adata)
sc.tl.leiden(adata, resolution=0.5)
scv.umap(adata, "leiden", legend_loc="right")
For a fuller worked example, see the notebook in test/scuva_test.ipynb.
Core workflows
UMAP plots
Use umap for a single panel.
scv.umap(adata, "leiden", legend_loc="right")
scv.umap(adata, "MS4A1", vcenter=0, legend_loc="right")
Behavior to be aware of:
- If
featureis categorical,scuvauses categorical colors and draws either a side legend or labels directly on the embedding. - If
featureis continuous,scuvabuilds a colorbar from the value range and treats zeros as background when plotting the colored layer. bottom_pointscan be a boolean mask or an array of indices and is useful for drawing selected cells underneath the rest.layeranduse_raw=Trueare mutually exclusive.
Use multiple_umap to compare several features or several AnnData objects in one figure.
scv.multiple_umap(adata, ["leiden", "MS4A1"], legend_loc="right")
Current behavior to keep in mind:
legend_locmay be a single value or a list that is cycled across panels.multiple_umapcurrently lays out at most two plotted panels per row reliably, soncolsshould stay at1or2.bottom_pointsis available onumap, butmultiple_umapdoes not expose that control to individual panels.
Use umap_split to create one panel per group.
scv.umap_split(
adata,
feature="leiden",
group_key="sample",
legend_loc="vertical",
figsize=(10, 4),
)
umap_split uses the order returned by adata.obs[group_key].unique() when building panels. For continuous features it applies one shared color scale across all panels. For categorical features, the subplot colors come from the usual AnnData-stored or default category colors.
Composition plots
graph_counts plots raw counts and graph_proportions plots percentages.
scv.graph_counts(adata, hue="sample", x="leiden", stack=True)
scv.graph_proportions(adata, x="sample", y="leiden", figsize=(3, 6))
These functions return the summary table together with the figure and axes, which makes them easy to reuse in reports or downstream scripts.
Marker inspection
Use get_expression_by_obs to run scanpy.tl.rank_genes_groups and attach two summary columns to the ranked-gene output:
percent_expressing: percentage of cells with nonzero raw counts.average_expression:log1p(mean(expression))for the requested expression source.
If layer=None, the function assumes adata.X contains integer count data and does not reverse any prior log1p transform.
For a typical Scanpy workflow where adata.X contains normalized values, pass the layer name that contains raw integer count data.
markers = scv.get_expression_by_obs(adata, "leiden", layer="counts")
scv.check_expression(
adata,
markers,
features=["MS4A1", "CD79A"],
cluster_column="leiden",
score_threshold=2,
)
check_expression prints the selected marker rows and then draws a small UMAP panel set containing the grouping column plus each requested gene.
Colors and labels
You can override category colors explicitly:
scv.set_categorical_colors(
adata,
"leiden",
{
"0": "red",
"1": "orange",
"2": "blue",
},
)
You can also provide a shared renaming dictionary for display text:
adata.uns["rename_dict"] = {
"leiden": "cluster",
"MS4A1": "CD20",
"sample_0": "control",
}
That renaming is applied by helpers such as rename, umap, graph_counts, and graph_proportions when generating titles and labels.
Public API
Plotting
umap(adata, feature, use_raw=False, layer=None, legend_loc="on data", vmin=None, vmax=None, vcenter=None, bottom_points=None, ...)multiple_umap(adata, features, layer=None, ncols=2, legend_loc="on data", ...)umap_split(adata, feature, group_key, legend_orientation="horizontal", ncols=2, ...)graph_counts(adata, hue, x, stack=False, sort_by_size=True, ...)graph_proportions(adata, x, y, x_order="sort", combine_small_percentages_other=False, ...)
Analysis
get_expression_by_obs(adata, column, layer="counts")check_expression(adata, expression_by_obs_df, features, cluster_column="cluster", cluster_subset=None, score_threshold=None)
Legends and color helpers
set_categorical_colors(adata, feature, color_mapping)get_categorical_colormap(adata, feature)make_legend(ax, title, label_color_dict, sort_ints="forward", ...)make_colorbar(sm, cax, label, orientation="vertical", ...)subplots_with_legend_axis(fig, total_subplots, nrows, ncols, side_ax_orientation, side_ax_proportion, use_extra_subplot_axis=True)
Text helpers
wrap_join(items, sep=" ", width=30)rename(adata, t, additional_renaming=None)clean_title(s)
Scope and limitations
This project is still small and evolving. A few details are worth calling out explicitly:
- The API is oriented around exploratory plotting rather than a full declarative plotting system.
- The plotting helpers assume a 2D UMAP-like embedding and do not try to generalize to arbitrary coordinate systems.
- The package leans on Scanpy conventions instead of re-implementing its own metadata model.
Contributing
Issues and pull requests are welcome. The project is not yet stable. Parts of the API may still change.
License
This code is licensed under GPL-3.0-or-later. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scuva-0.1.1.tar.gz.
File metadata
- Download URL: scuva-0.1.1.tar.gz
- Upload date:
- Size: 37.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b6f61713eb964e05dfdc06766bd9889e466db9989890368904fbf06fa2f69aa
|
|
| MD5 |
111c3a48fdf11303c192a95ee179f332
|
|
| BLAKE2b-256 |
f639d377c6e3f460f8c671c46892f3255e6e72107799fd6a0804c197d4ed16e1
|
File details
Details for the file scuva-0.1.1-py3-none-any.whl.
File metadata
- Download URL: scuva-0.1.1-py3-none-any.whl
- Upload date:
- Size: 37.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b8bd8db29a0cb5934d67d586d353852f681d7802ea39f2739ca15a8fb3e25a1
|
|
| MD5 |
ecdacb9e529c131a6e9ac09d8d3714d3
|
|
| BLAKE2b-256 |
36996a5071b0c39f9453976d5d18f1313c7d35ea48c5ae08a8dcf04692132976
|