Embeddings#

First of five demonstration notebooks showing variations of scplotkit’s major plot families – this one covers UMAP-based plots: masked_umap, masked_umap_highlight, gene_expression_umap, gene_coexpression_umap, and embedding_density. Each function is shown with a few different parameter combinations rather than one canonical call each, so the range of what’s tunable is visible directly in the output instead of only in the API docs.

Data: a subset of the recently published PDAC atlas by Lucarelli et al. (https://doi.org/10.64898/2026.03.19.712924). See Composition, Overview, Markers & expression, and Hierarchy & enrichment for the other demonstration notebooks, or API Reference for the complete parameter reference.

import anndata as ad
import scanpy as sc

import scplotkit as spk

sc.settings.verbosity = 1

ATLAS_PATH = "/data/Daniele/atlases/final_versions_topublish/Human_subsampled.zarr"

Load the atlas#

adata = ad.read_zarr(ATLAS_PATH)
adata
AnnData object with n_obs × n_vars = 100000 × 39041
    obs: 'Sample_ID', 'Condition', 'Treatment', 'TreatmentType', 'TreatmentStatus', 'Tissue', 'Sex', 'Dataset', 'Technology', 'Level_1', 'Level_2', 'Level_3', 'Level_4', 'Age', 'Diabetes', 'Is_Core', 'EMT category', 'Dataset_ID', 'Cluster_Names'
    var: 'n_cells', 'ensembl_id', 'start', 'end', 'chromosome', 'gene_name_adata_sc', 'highly_variable_adata_sc', 'means_adata_sc', 'dispersions_adata_sc', 'dispersions_norm_adata_sc', 'highly_variable_nbatches_adata_sc', 'highly_variable_intersection_adata_sc', 'n_cells_by_counts_adata_sc', 'mean_counts_adata_sc', 'log1p_mean_counts_adata_sc', 'pct_dropout_by_counts_adata_sc', 'total_counts_adata_sc', 'log1p_total_counts_adata_sc', 'mito_adata_sc', 'n_cells_by_counts_adata_sn', 'mean_counts_adata_sn', 'log1p_mean_counts_adata_sn', 'pct_dropout_by_counts_adata_sn', 'total_counts_adata_sn', 'log1p_total_counts_adata_sn', 'Manual_Genes', 'mt', 'ribo', 'hb'
    uns: 'Condition_colors', 'UMAP_0.85', 'Level_3_colors', 'umap', '_scvi_manager_uuid', 'Is_Core_colors', 'Level_4_colors', 'neighbors', 'UMAP_0.75', '_scvi_uuid', 'Level_2_colors', 'Level_1_colors', 'Level_4_Final_colors', 'Level_4_All_colors'
    obsm: 'log_counts', 'is_outlier_total_counts', 'X_pca', 'pct_counts_mito', 'scanvi_extended_atlas_emb', 'UMAP_0.85', 'bin_edges', 'scanvi_L4_emb', 'scANVI_cross_species', 'level0_leiden_subcluster', '_scvi_batch', 'leiden_0.2_annotation', 'n_genes_by_counts', 'log1p_total_counts', 'batch', 'n_genes', 'total_counts', 'outlier', 'log1p_n_genes_by_counts', 'leiden', 'EMT_score_DL', 'X_umap', 'total_counts_mito', 'Global_Leiden', 'UMAP_0.75', 'MALAT1_lognorm', 'infercnv_score_malignant', 'log1p_total_counts_mito', 'UMAP_0.95', 'leiden_0.5', 'leiden_0.2', 'EMT score', 'scANVI_emb_final', 'cnv_score_abs', 'leiden_subcluster', 'empty_droplet', 'n_counts', 'mt_frac', 'infercnv_score_malignant_refined', '_scvi_labels'
    layers: 'raw', 'log_norm', 'counts'
    obsp: 'distances', 'connectivities'

Set up the plotter#

config = spk.PlotConfig({
    "palettes": {
        "Level_2": {
            "Lymphoid": "#1f78b4",
            "Malignant Cell": "#e31a1c",
            "Stromal Cell": "#33a02c",
            "Myeloid": "#ff7f00",
            "Endothelial Cell": "#6a3d9a",
            "Exocrine Cell": "#b15928",
            "Endocrine Cell": "#a6cee3",
        },
        "TreatmentStatus": {"Untreated": "#4daf4a", "Treated": "#984ea3"},
    }
})
plotter = spk.ScPlotter(config=config, output_dir="figures")

masked_umap#

Colors color_by categories inside mask_values, greying out everything else (a third, subset-only variant is also saved by every call). Two variations: a broad multi-category mask, and a compartment made up of several smaller Level_3 subtypes – see masked_umap_highlight below for populations that are hard to spot without enlarging them first.

plotter.masked_umap(
    adata,
    color_by="Level_2",
    mask_values=["Malignant Cell", "Stromal Cell"],
    figure_name="Tumor microenvironment",
    show=True,
)
WARNING: saving figure to file figures/Tumor_microenvironment/umap_Tumor_microenvironment_masked_colored_Level_2.png
../_images/f78f7780610db9d6f8c5c5d702ab8948be6e812bcf18371d7d7af191910a8788.png
WARNING: saving figure to file figures/Tumor_microenvironment/umap_Tumor_microenvironment_masked_colored_Level_2_legend.png
../_images/61f27e999008ab466c5331f8b74726dd8208735184f7535c1dad9704fdd2eb76.png
WARNING: saving figure to file figures/Tumor_microenvironment/umap_Tumor_microenvironment_subset_only_Level_2_legend.png
../_images/5dff6f6b28d7267b24ae2f3e629cc64d3274bf73bbbf18a985b917099caac731.png

The myeloid compartment at Level_3 resolution (macrophages, dendritic cells, neutrophils, monocytes) – several distinct subtypes grouped into one mask.

plotter.masked_umap(
    adata,
    color_by="Level_3",
    mask_values=["Macrophage", "Dendritic Cell", "Neutrophil", "Monocyte"],
    figure_name="Myeloid compartment",
    show=True,
    size=2
)
WARNING: saving figure to file figures/Myeloid_compartment/umap_Myeloid_compartment_masked_colored_Level_3.png
../_images/46420ee13919665f9bb52fdeed8d24bad075815d741b5f7609c89cbcfa851509.png
WARNING: saving figure to file figures/Myeloid_compartment/umap_Myeloid_compartment_masked_colored_Level_3_legend.png
/home/philipp.putze/github_repos/singleplot/.venv/lib/python3.11/site-packages/scanpy/plotting/_tools/scatterplots.py:1148: UserWarning: *c* argument looks like a single numeric RGB or RGBA sequence, which should be avoided as value-mapping will have precedence in case its length matches with *x* & *y*.  Please use the *color* keyword-argument or provide a 2D array with a single row if you intend to specify the same RGB or RGBA value for all points.
  ax.scatter([], [], c=palette[label], label=label)
../_images/d4a4d069d59f227403a7582dd6082e14f70dbde50446871db70348ae09378148.png
WARNING: saving figure to file figures/Myeloid_compartment/umap_Myeloid_compartment_subset_only_Level_3_legend.png
/home/philipp.putze/github_repos/singleplot/.venv/lib/python3.11/site-packages/scanpy/plotting/_tools/scatterplots.py:1148: UserWarning: *c* argument looks like a single numeric RGB or RGBA sequence, which should be avoided as value-mapping will have precedence in case its length matches with *x* & *y*.  Please use the *color* keyword-argument or provide a 2D array with a single row if you intend to specify the same RGB or RGBA value for all points.
  ax.scatter([], [], c=palette[label], label=label)
../_images/3a5697b529bdaddc818950713f46e01afb4d863baefd3f7fe37fb7f239c9daf9.png

masked_umap_highlight#

Draws the masked population enlarged on top of a small greyed-out background instead of hiding the rest – especially useful for populations that are hard to spot with plain masking above, either because they’re spread across several smaller subtypes or because they’re rare. Four variations: the myeloid subtypes from above with a custom palette, a rare population (endocrine cells), an extreme highlight/background size contrast on a single subtype, and a subtle highlight with ordered=False.

plotter.masked_umap_highlight(
    adata,
    color_by="Level_3",
    mask_values=["Macrophage", "Dendritic Cell", "Neutrophil", "Monocyte"],
    figure_name="Myeloid subtypes (custom palette)",
    palette={
        "Macrophage": "#d95f02",
        "Dendritic Cell": "#7570b3",
        "Neutrophil": "#e7298a",
        "Monocyte": "#66a61e",
    },
    highlight_size=6,
    background_size=1.5,
    show=True,
)
WARNING: saving figure to file figures/Myeloid_subtypes_(custom_palette)/umap_Myeloid_subtypes_(custom_palette)_masked_colored_Level_3_6_highlight.png
../_images/5cbf93db4d2f2c05721d8c777bbf21db716ce359fa0f7a8a1b0baa0de23d7e42.png
WARNING: saving figure to file figures/Myeloid_subtypes_(custom_palette)/umap_Myeloid_subtypes_(custom_palette)_masked_colored_Level_3_6_highlight_legend.png
../_images/4f70ab4d90b59a474968fe83263e2d8b122563de4e1f1db5d56976589d5c803f.png

Endocrine cells make up a small fraction of a PDAC atlas – enlarging them makes the population obvious in a way plain masking above does not.

plotter.masked_umap_highlight(
    adata,
    color_by="Level_2",
    mask_values=["Endocrine Cell"],
    figure_name="Endocrine cells (rare population)",
    highlight_size=20,
    background_size=0.5,
    show=True,
)
WARNING: saving figure to file figures/Endocrine_cells_(rare_population)/umap_Endocrine_cells_(rare_population)_masked_colored_Level_2_20_highlight.png
../_images/2016e3dd23f42954e09ded109a4c0fb00026f52b212f536ca89d4aa894bdd488.png
WARNING: saving figure to file figures/Endocrine_cells_(rare_population)/umap_Endocrine_cells_(rare_population)_masked_colored_Level_2_20_highlight_legend.png
../_images/c5e2a11cb7597f0a2d534bd5e4e4e4571b32e7d3dca99d1e7a108d66672d79e9.png

A subtler highlight (sizes close together) with ordered=False, so highlighted and background cells are drawn in their original row order instead of highlighted-on-top – some highlighted cells can end up covered by background ones.

plotter.masked_umap_highlight(
    adata,
    color_by="Level_2",
    mask_values=["Lymphoid"],
    figure_name="Lymphoid (subtle highlight, unordered)",
    highlight_size=2.5,
    background_size=1.5,
    ordered=False,
    show=True,
)
WARNING: saving figure to file figures/Lymphoid_(subtle_highlight,_unordered)/umap_Lymphoid_(subtle_highlight,_unordered)_masked_colored_Level_2_2.5_highlight.png
../_images/1c4ebd985f26e0d358c1c98a4d97d60235e1dad9d7ffa9c77be6624ba76c817d.png
WARNING: saving figure to file figures/Lymphoid_(subtle_highlight,_unordered)/umap_Lymphoid_(subtle_highlight,_unordered)_masked_colored_Level_2_2.5_highlight_legend.png
../_images/7bd92ed6d1b7a13962f59095227ea3ec55496365c9934aee0410d333c19836f7.png

gene_expression_umap#

Expression of one gene on the embedding, optionally masked to a subset of cells. Four variations covering colormap choice and value scaling: the default sequential colormap with an automatic scale, a different sequential colormap with percentile-based clipping, a diverging colormap on a masked subset, and manually fixed vmin/vmax.

plotter.gene_expression_umap(
    adata,
    gene="EPCAM",
    layer="log_norm",
    mask_column="Level_2",
    mask_values=["Malignant Cell"],
    figure_name="EPCAM_in_malignant_cells",
    show=True,
)
Masking applied: showing EPCAM only in ['Malignant Cell']
WARNING: saving figure to file figures/EPCAM_in_malignant_cells/umap_EPCAM_in_malignant_cells_EPCAM_masked_Level_2.png
../_images/8ab1f33e3b67a5dd69bdbe3f7a29738b2aa4612f03d03f7738620c93e7240fcb.png
WARNING: saving figure to file figures/EPCAM_in_malignant_cells/umap_EPCAM_in_malignant_cells_EPCAM_masked_Level_2_with_colorbar.png
../_images/a088d5157c2cd5904f3a5356c7b519f5b66b675ab4cb4a858066bdf596579721.png

cmap="viridis" over the whole atlas (no mask), with vmax="p99" clipping the top percentile of expression so a handful of very high cells don’t wash out the rest of the scale.

plotter.gene_expression_umap(
    adata,
    gene="CD8A",
    layer="log_norm",
    cmap="viridis",
    vmax="p99",
    figure_name="CD8A_all_cells_viridis",
    show=True,
)
WARNING: saving figure to file figures/CD8A_all_cells_viridis/umap_CD8A_all_cells_viridis_CD8A_all_cells.png
../_images/6301aef83b6e62195cec40be86d10d030f351b630a26b7526f66d6e87809227e.png
WARNING: saving figure to file figures/CD8A_all_cells_viridis/umap_CD8A_all_cells_viridis_CD8A_all_cells_with_colorbar.png
../_images/b5d0b70446fcbd4c310a4c156477a80d1cdf5323b2269ea394c56edffe0d124f.png

A diverging colormap (coolwarm) on a masked subset – diverging colormaps are usually reserved for signed values (e.g. log fold change), but work here too as a higher-contrast alternative to a sequential one.

plotter.gene_expression_umap(
    adata,
    gene="COL1A1",
    layer="log_norm",
    mask_column="Level_2",
    mask_values=["Stromal Cell"],
    cmap="coolwarm",
    figure_name="COL1A1_in_stromal_cells",
    show=True,
)
Masking applied: showing COL1A1 only in ['Stromal Cell']
WARNING: saving figure to file figures/COL1A1_in_stromal_cells/umap_COL1A1_in_stromal_cells_COL1A1_masked_Level_2.png
../_images/7cc48e8d5f1246751490322108eab9ec3dbf652f2bb49d132b9d866ae685ca09.png
WARNING: saving figure to file figures/COL1A1_in_stromal_cells/umap_COL1A1_in_stromal_cells_COL1A1_masked_Level_2_with_colorbar.png
../_images/f8ebde6282acb2a8ee2f6414e5d84a595ed25b90395402b5ac6ca20c5ab83c1b.png

Manually fixed vmin/vmax instead of the percentile-based default – useful when comparing the same gene across multiple figures/panels that need an identical color scale.

plotter.gene_expression_umap(
    adata,
    gene="PDGFRB",
    layer="log_norm",
    vmin=0,
    vmax=3,
    figure_name="PDGFRB_fixed_scale",
    show=True,
)
WARNING: saving figure to file figures/PDGFRB_fixed_scale/umap_PDGFRB_fixed_scale_PDGFRB_all_cells.png
../_images/565102fa95584d33c1fdf4bd56810ecbf5f59f91024487a56a67150cb4553fec.png
WARNING: saving figure to file figures/PDGFRB_fixed_scale/umap_PDGFRB_fixed_scale_PDGFRB_all_cells_with_colorbar.png
../_images/3992d1dd6d6011fdb93ab84a38a39cdebd4803f68fe023de8dcf6621920b0fe6.png

gene_coexpression_umap#

Blends two genes’ normalized expression into one bivariate color per cell. Three variations: the default magenta/green blend, a custom color pair on a different marker duo, and a variant tuned via threshold/ point_size/add_outline.

Each call ends with a ; – the function both shows the figure itself (show=True) and returns it, and without the ; Jupyter’s automatic display of the cell’s last expression would render the same (already shown) figure a second time.

plotter.gene_coexpression_umap(
    adata,
    gene1="CD8A",
    gene2="CD4",
    layer="log_norm",
    mask_column="Level_2",
    mask_values=["Lymphoid"],
    figure_name="CD4CD8_gradient",
    show=True,
);
Masking applied: showing CD8A/CD4 co-expression only in ['Lymphoid']
Figure saved to: figures/CD4CD8_gradient/CD4CD8_gradient_CD8A_CD4_coexpression.png
../_images/23a6778b82e94fa6c59d969339a3080286c9309a88d3b5db05a37e7a171a1c2c.png

Custom color1/color2 for the myCAF (PDGFRB-high) vs. iCAF (COL1A1-only) fibroblast distinction within the stromal compartment.

plotter.gene_coexpression_umap(
    adata,
    gene1="PDGFRB",
    gene2="COL1A1",
    color1="#1b7837",
    color2="#e08214",
    layer="log_norm",
    mask_column="Level_2",
    mask_values=["Stromal Cell"],
    figure_name="myCAF_iCAF_gradient",
    show=True,
);
Masking applied: showing PDGFRB/COL1A1 co-expression only in ['Stromal Cell']
Figure saved to: figures/myCAF_iCAF_gradient/myCAF_iCAF_gradient_PDGFRB_COL1A1_coexpression.png
../_images/d664c4e1b35368368c9738fc0d3e11558f00c607eb98a9a776a51547222f78cf.png

Same CD4/CD8 pair, but with a lower threshold (fewer cells drop out to grey), a fixed point_size, and add_outline=False for a flatter look on a dense embedding.

plotter.gene_coexpression_umap(
    adata,
    gene1="CD8A",
    gene2="CD4",
    layer="log_norm",
    mask_column="Level_2",
    mask_values=["Lymphoid"],
    threshold=0.02,
    point_size=6,
    add_outline=False,
    figure_name="CD4CD8_gradient_flat",
    show=True,
);
Masking applied: showing CD8A/CD4 co-expression only in ['Lymphoid']
Figure saved to: figures/CD4CD8_gradient_flat/CD4CD8_gradient_flat_CD8A_CD4_coexpression.png
../_images/f6e897e7ec6fb789e91f9272255ed5d4f38825ddd7a3cfa6f0124afa31830707.png

embedding_density#

Per-cell density, optionally one panel per group. Three variations: a single overall panel, grouped by TreatmentStatus, and grouped by a broader column with a different colormap and a sharper bandwidth. As above, each call ends with a ; to avoid a duplicate render of the already-shown figure.

plotter.embedding_density(
    adata,
    figure_name="Overall cell density",
    save_name="density_overall",
    show=True,
);
Figure saved to: figures/embedding_density/density_overall.png
../_images/986fa2a391d9daca58c38befd9f20a9d680720d7ded7c380cf2369364954160e.png
plotter.embedding_density(
    adata,
    groupby="TreatmentStatus",
    figure_name="PDAC density by treatment status",
    save_name="density_by_treatment_status",
    show=True,
);
Figure saved to: figures/embedding_density/density_by_treatment_status.png
../_images/45965fe5c6960bd5c09286ed5bd1ba0b61ec2d8696fc30ea045a77b0616112e6.png

Grouped by Level_1 instead, with a different colormap and a sharper (bw_scale) density surface – a lower bw_scale picks out smaller, more localized hotspots instead of smoothing them into broad regions.

plotter.embedding_density(
    adata,
    groupby="Level_1",
    cmap="viridis",
    bw_scale=0.25,
    figure_name="Density by broad compartment (sharper)",
    save_name="density_by_level1_sharp",
    show=True,
);
Figure saved to: figures/embedding_density/density_by_level1_sharp.png
../_images/0d3e694879c66e8328a6ba024fe5c9f856f35a638070d8103d2c298995fc29ee.png

Next steps#

Every plot above was also written to disk under figures/, one subdirectory per plot family. See Composition for composition plots, or API Reference for the full parameter reference of every function used here.