sc_tools.pl — Plotting#
Plotting utilities for spatial omics data following the scanpy API pattern.
import sc_tools.pl as pl
pl.multipage_spatial_pdf(adata, keys=["Hallmark/HYPOXIA"], output_path="out.pdf")
pl.qc_2x2_grid(adata)
pl.plot_gsea_dotplot(results_df)
Spatial Plots#
- sc_tools.pl.spatial.plot_spatial_plain_he(adata, library_id, ax, image_key='hires')[source]#
Plot plain H&E tissue image for a library (no spots overlay).
- sc_tools.pl.spatial.plot_spatial_categorical(adata, library_id, color, ax, title=None, palette=None, legend_loc='right margin', frameon=False, **kwargs)[source]#
Plot spatial overlay of a categorical variable (e.g. annotation, solidity).
- Parameters:
adata (AnnData) – Subset AnnData for this library (e.g. adata[adata.obs[‘library_id’] == library_id]).
library_id (str) – Key in adata.uns[‘spatial’].
color (str) – Column name in adata.obs (categorical).
ax (Axes) – Matplotlib axes.
legend_loc (str) – Passed to scanpy (default ‘right margin’).
frameon (bool) – Passed to scanpy (default False).
**kwargs (
Any) – Passed to sc.pl.spatial.
- Return type:
- sc_tools.pl.spatial.plot_spatial_continuous(adata, library_id, color, ax, title=None, cmap='coolwarm', vmin=None, vmax=None, frameon=False, values=None, **kwargs)[source]#
Plot spatial overlay of a continuous variable (e.g. score).
- Parameters:
adata (AnnData) – Subset AnnData for this library.
library_id (str) – Key in adata.uns[‘spatial’].
color (str) – Column name in adata.obs (numeric). Ignored if values is provided.
ax (Axes) – Matplotlib axes.
title (str, optional) – Axis title. If None, uses color or “Score”.
cmap (str) – Colormap name (default ‘coolwarm’).
frameon (bool) – Passed to scanpy (default False).
values (Series or ndarray, optional) – If provided, use these values for the overlay (length/index must match adata.obs_names). Use when scores are in obsm instead of obs.
**kwargs (
Any) – Passed to sc.pl.spatial.
- Return type:
- sc_tools.pl.spatial.multipage_spatial_pdf(adata, library_id_col, panels, output_path, figsize=(18, 12), dpi=300)[source]#
Create a multipage PDF with one page per library and N spatial panels per page.
- Parameters:
adata (AnnData) – Full AnnData with obs[library_id_col], uns[‘spatial’], and any obs columns required by the panels.
library_id_col (str) – Column in adata.obs that identifies the library/sample.
panels (list of dict) – List of panel specs. Each dict must have a
"type"key ("he","categorical", or"continuous")."he"needs no extra keys."categorical"needs"obs_col"and"title"(optional"palette")."continuous"needs"title"and either"obs_col"or"values"(optional"cmap","vmin","vmax").output_path (str) – Path to the output PDF file.
figsize (tuple) – Figure size per page (default (18, 12)).
dpi (int) – DPI for saved pages (default 300).
- Return type:
Heatmaps#
- sc_tools.pl.heatmaps.signature_score_heatmap(adata, sig_columns, annotation_cols, sort_by, category_orders=None, cluster=False, sig_prefix='sig:', sig_suffix='_z', vmin=-3, vmax=3, figsize=None, solidity_colors_hex=None, legend_title=None)[source]#
Build heatmap or clustermap of signature scores with annotation bars.
Annotation columns are given as display_name -> obs column name. sort_by is list of display names (primary, secondary). category_orders maps display name -> ordered list of categories; columns not in it are ordered by mean score (descending).
- Parameters:
adata (AnnData) – Object with signature scores in obs[sig_columns] and annotation columns.
sig_columns (list) – Obs column names for signature scores.
annotation_cols (dict) – Display name -> obs column name, e.g. {‘Patient’: ‘library_id’, ‘Solidity’: ‘tumor_type’}.
sort_by (list) – [primary, secondary] display names for sorting.
category_orders (dict, optional) – Display name -> list of category order. Missing names: order by mean score.
cluster (bool) – If True, build clustermap with within-group clustering; else heatmap only.
sig_prefix (str) – Stripped from sig_columns for row labels.
sig_suffix (str) – Stripped from sig_columns for row labels.
vmin (float) – Color scale for score matrix.
vmax (float) – Color scale for score matrix.
figsize (tuple, optional) – (width, height). Default heatmap (16,12), clustermap (18,14).
solidity_colors_hex (dict, optional) – For backward compatibility: category -> hex for second annotation (e.g. Solidity).
legend_title (str, optional) – Title for legend (e.g. ‘Solidity’).
- Return type:
- Returns:
fig (Figure) – The figure (caller can save with st.pl.save_figure).
g (seaborn.ClusterGrid or None) – If cluster=True, the ClusterGrid for further tweaks; else None.
- sc_tools.pl.heatmaps.cluster_within_groups(data_matrix, group_labels, method='average', metric='euclidean')[source]#
Cluster rows within each group; preserve group order.
- Parameters:
- Returns:
Reordered row indices (cluster within each group, groups in order).
- Return type:
np.ndarray
- sc_tools.pl.heatmaps.annotation_colors_from_categories(annotations, column_colors=None, default_hex=None)[source]#
Build per-column color lists (RGB) for annotation bars.
- Parameters:
annotations (pd.DataFrame) – Index = sample ids, columns = annotation names; values = category labels.
column_colors (dict, optional) – Maps column name -> {category: (r,g,b)}. If None, uses seaborn Set3 for each column.
default_hex (dict, optional) – Maps column name -> {category: hex}. Converted to RGB; overridden by column_colors.
- Returns:
column name -> list of (r,g,b) in row order.
- Return type:
- sc_tools.pl.heatmaps.get_obs_category_colors(adata, obs_col, store_if_missing=True)[source]#
Get category -> RGB color mapping for a categorical obs column using the scanpy convention: adata.uns[f’{obs_col}_colors’] is a list of hex strings (one per category in order). If missing or length mismatch, create a default palette and optionally store it in adata.uns.
- Parameters:
adata (AnnData) – Object with obs[obs_col] categorical and optionally uns[f’{obs_col}_colors’].
obs_col (str) – Name of the categorical column in adata.obs.
store_if_missing (bool) – If True (default), when colors are missing or invalid, create a palette and set adata.uns[f’{obs_col}_colors’] to a list of hex strings.
- Returns:
Map from category value to (r, g, b) in [0, 1]. None if obs_col is not present or not categorical.
- Return type:
dict or None
Enrichment#
- sc_tools.pl.plot_gsea_dotplot(result_df, top_n=10, groups=None, figsize=(8, 6), output_path=None)[source]#
Dot plot of gene set enrichment results.
Dot size encodes statistical significance (-log10 adjusted p-value); dot color encodes normalized enrichment score (NES) or odds ratio. Compatible with output from both
run_ora()andrun_gsea_pseudobulk().- Parameters:
result_df (pd.DataFrame) – Long-format enrichment results. Must contain at minimum the columns
group,gene_set,p_adj. Optional:NES(used for color if present, otherwiseodds_ratiois used, then defaults to black).top_n (int) – Top N gene sets to display per group (ranked by p_adj ascending). Default 10.
groups (list[str] or None) – Subset of groups to display. Defaults to all groups in result_df.
figsize (tuple) – Figure size in inches (width, height). Default (8, 6).
output_path (str, Path, or None) – If provided, save the figure to this path at 300 DPI. Parent directories are created if needed.
- Returns:
The figure object.
- Return type:
QC Plots#
Re-exported from sc_tools.qc.plots for st.pl.qc_* usage.
See sc_tools.qc — Quality Control for full documentation.
- sc_tools.pl.qc_2x2_grid(adata, *, total_counts_col='total_counts', n_genes_col='n_genes_by_counts', pct_mt_col='pct_counts_mt', output_dir=None, basename='qc_2x2', dpi=300, figsize=(10, 10), modality='visium')[source]#
Plot 2x2 QC grid: total_counts, n_genes, log1p(total_counts), pct_counts_mt.
Panels: (1,1) total_counts histogram, (1,2) n_genes_by_counts histogram, (2,1) log1p(total_counts) histogram, (2,2) pct_counts_mt histogram if present, else log1p(n_genes_by_counts).
- Parameters:
adata (AnnData) – Annotated data with obs containing total_counts and n_genes_by_counts (from calculate_qc_metrics). pct_counts_mt optional.
total_counts_col (str) – Obs column for total counts (default ‘total_counts’).
n_genes_col (str) – Obs column for number of genes (default ‘n_genes_by_counts’).
pct_mt_col (str) – Obs column for percent mitochondrial (default ‘pct_counts_mt’).
output_dir (str or Path or None) – If set, save PDF and PNG here (default None).
basename (str) – Base name for files (default ‘qc_2x2’).
dpi (int) – DPI for PNG (default 300).
figsize (tuple) – Figure size (default (10, 10)).
modality (str)
- Returns:
The figure (caller may show or save).
- Return type:
- sc_tools.pl.qc_2x4_pre_post(adata_pre, adata_post, *, total_counts_col='total_counts', n_genes_col='n_genes_by_counts', pct_mt_col='pct_counts_mt', output_dir=None, basename='qc_2x4_pre_post', dpi=300, figsize=(16, 10), modality='visium')[source]#
Plot pre- vs post-filter QC: 2 rows x 4 columns. Left 2x2 = pre-filter (raw) metrics; right 2x2 = post-filter metrics. Use this so post-filter distributions (e.g. after filter_cells/filter_genes) are directly comparable to pre.
Panels: row0 = total_counts (pre), n_genes (pre) | total_counts (post), n_genes (post); row1 = log1p(total_counts) (pre), pct_mt (pre) | log1p(total_counts) (post), pct_mt (post).
- Parameters:
adata_pre (AnnData) – Pre-filter (raw) adata with total_counts, n_genes_by_counts (and optionally pct_counts_mt).
adata_post (AnnData) – Post-filter (and optionally normalized) adata. Should have been filtered so that n_obs and metric distributions differ from pre. Must have same obs column names.
total_counts_col (str) – Obs column for total counts (default ‘total_counts’).
n_genes_col (str) – Obs column for number of genes (default ‘n_genes_by_counts’).
pct_mt_col (str) – Obs column for percent mitochondrial (default ‘pct_counts_mt’).
output_dir (str or Path or None) – If set, save PDF and PNG here (default None).
basename (str) – Base name for files (default ‘qc_2x4_pre_post’).
dpi (int) – DPI for PNG (default 300).
figsize (tuple) – Figure size (default (16, 10)).
modality (str)
- Returns:
The figure.
- Return type:
- sc_tools.pl.qc_spatial_multipage(adata, library_id_col, output_path, *, total_counts_col='total_counts', pct_mt_col='pct_counts_mt', figsize=(18, 6), dpi=300, common_scale=True)[source]#
Multipage spatial QC report: one page per sample with 1x3 panels (total_count, log1p(total_count), % mt).
When common_scale is True (default), the same vmin/vmax is used for each metric across all pages so color scales are comparable across samples.
Requires adata.obs[library_id_col], adata.obsm[‘spatial’], and adata.uns[‘spatial’] with per-library images for spatial plots.
- Parameters:
adata (AnnData) – Annotated data with spatial coords and (optionally) H&E in uns[‘spatial’].
library_id_col (str) – Column in adata.obs identifying library/sample.
output_path (str or Path) – Path to output PDF (e.g. figures/QC/raw/qc_spatial_multipage.pdf).
total_counts_col (str) – Obs column for total counts (default ‘total_counts’).
pct_mt_col (str) – Obs column for percent mitochondrial (default ‘pct_counts_mt’).
figsize (tuple) – Figure size per page (default (18, 6)).
dpi (int) – DPI for saved PDF (default 300).
common_scale (bool) – If True, use global vmin/vmax (99th percentile) per metric across all spots so every page uses the same color scale (default True).
- Return type:
- sc_tools.pl.qc_violin_metrics(adata, *, keys=None, groupby=None, output_dir=None, basename='qc_violin', dpi=300, figsize=None)[source]#
Multi-panel violin plot for QC metrics (n_genes_by_counts, total_counts, pct_counts_mt).
Uses scanpy’s violin with show=False and captures the figure for saving. Requires adata.obs columns from calculate_qc_metrics.
- Parameters:
adata (AnnData) – Annotated data with obs containing total_counts, n_genes_by_counts, pct_counts_mt.
keys (list of str or None) – Obs columns to plot (default: n_genes_by_counts, total_counts, pct_counts_mt).
groupby (str or None) – Optional obs column to stratify violins (e.g. library_id, sample).
output_dir (str or Path or None) – If set, save PDF and PNG here.
basename (str) – Base name for files (default ‘qc_violin’).
dpi (int) – DPI for PNG (default 300).
figsize (tuple or None) – Figure size; if None, scanpy default is used.
- Return type:
- sc_tools.pl.qc_scatter_counts_genes(adata, *, x='total_counts', y='n_genes_by_counts', color='pct_counts_mt', output_dir=None, basename='qc_scatter', dpi=300, figsize=(6, 5))[source]#
Scatter plot: total_counts (x) vs n_genes_by_counts (y), colored by pct_counts_mt.
Uses scanpy’s scatter with show=False. Requires adata.obs from calculate_qc_metrics.
- Parameters:
adata (AnnData) – Annotated data with obs columns for x, y, and color.
x (str) – Obs column names (defaults: total_counts, n_genes_by_counts, pct_counts_mt).
y (str) – Obs column names (defaults: total_counts, n_genes_by_counts, pct_counts_mt).
color (str) – Obs column names (defaults: total_counts, n_genes_by_counts, pct_counts_mt).
output_dir (str or Path or None) – If set, save PDF and PNG here.
basename (str) – Base name for files (default ‘qc_scatter’).
dpi (int) – DPI for PNG (default 300).
figsize (tuple) – Figure size (default (6, 5)).
- Return type:
- sc_tools.pl.plot_highly_variable_genes(adata, *, output_dir=None, basename='hvg', dpi=300, figsize=(6, 4))[source]#
Plot mean vs dispersion (or normalized dispersion) with highly variable genes highlighted.
Requires adata.var with ‘highly_variable’ and flavor-specific columns (means, dispersions or dispersions_norm). Uses sc.pl.highly_variable_genes(show=False).
- Parameters:
- Return type:
- sc_tools.pl.plot_spatially_variable_genes(adata, *, x_axis='mean', color_by='spatially_variable', output_dir=None, basename='svg', dpi=300, figsize=(6, 5))[source]#
Scatter: x = mean expression (or rank), y = Moran’s I (spatial_i), colored by spatially_variable or pval.
If adata.uns[‘spatial_variable_per_library’] exists (from spatially_variable_genes_per_library), one subplot per library is drawn. Otherwise requires adata.var with spatial_i.
- Parameters:
adata (AnnData) – Annotated data after spatially_variable_genes or with uns[‘spatial_variable_per_library’].
x_axis (str) – ‘mean’ or ‘rank’: x-axis (default ‘mean’).
color_by (str) – ‘spatially_variable’ or ‘spatial_pval’ (default ‘spatially_variable’).
output_dir (str or Path or None) – If set, save PDF and PNG here.
basename (str) – Base name for files (default ‘svg’).
dpi (int) – DPI for PNG (default 300).
figsize (tuple) – Figure size per panel (default (6, 5)).
- Return type:
- sc_tools.pl.qc_sample_comparison_bar(metrics, metric_cols=None, classified=None, output_dir=None, basename='qc_sample_comparison', dpi=300, log_scale=False)[source]#
Bar chart per metric, one bar per sample, sorted by value.
Failed samples (from
classified) are highlighted in red.- Parameters:
metrics (pd.DataFrame) – Output of
compute_sample_metrics(indexed by sample).metric_cols (list of str or None) – Columns to plot (default: n_genes_median, total_counts_median, pct_mt_median, n_spots).
classified (pd.DataFrame or None) – If provided (output of
classify_samples), failed samples shown in red.output_dir (str or Path or None) – If set, save PDF and PNG.
basename (str) – Base filename.
dpi (int) – DPI for PNG.
log_scale (bool) – If True, transform values with log10(x + 1) and annotate y-ticks with original-scale labels (default False).
- Return type:
- sc_tools.pl.qc_sample_violin_grouped(adata, sample_col='library_id', keys=None, classified=None, output_dir=None, basename='qc_sample_violin', dpi=300, log_scale=False)[source]#
Violin plots grouped by sample for direct distribution comparison.
- Parameters:
adata (AnnData) – Annotated data with QC columns in obs.
sample_col (str) – Column in obs identifying samples.
keys (list of str or None) – Obs columns to plot (default: n_genes_by_counts, total_counts, pct_counts_mt).
classified (pd.DataFrame or None) – If provided, failed sample names are marked with
(FAIL)suffix.output_dir (str or Path or None) – If set, save PDF and PNG.
basename (str) – Base filename.
dpi (int) – DPI for PNG.
log_scale (bool) – If True, apply log10(x + 1) to all keys except pct_counts_mt (which stays linear). Custom y-tick annotations are added (default False).
- Return type:
- sc_tools.pl.qc_sample_scatter_matrix(metrics, metric_cols=None, classified=None, output_dir=None, basename='qc_sample_scatter_matrix', dpi=300)[source]#
Pairwise scatter of sample-level metrics with pass/fail coloring.
- Parameters:
metrics (pd.DataFrame) – Output of
compute_sample_metrics.metric_cols (list of str or None) – Columns for scatter matrix (default: n_spots, n_genes_median, total_counts_median, pct_mt_median).
classified (pd.DataFrame or None) – If provided, color points by pass (blue) / fail (red).
output_dir (str or Path or None) – If set, save PDF and PNG.
basename (str) – Base filename.
dpi (int) – DPI for PNG.
- Return type:
Utilities#
- sc_tools.pl.save_figure(fig, basename, output_dir, dpi=300, dt=None, bbox_inches='tight', pad_inches=0.1, create_pdf_png_folders=True)[source]#
Save a figure in two formats with a versioned filename under pdf/ and png/.
- Creates:
output_dir/pdf/YYDDMM.hh.mm.basename.pdf
output_dir/png/YYDDMM.hh.mm.basename.png
- Parameters:
fig (matplotlib.figure.Figure) – Figure to save.
basename (str) – Base name (no extension), e.g. “volcano_faceted”.
output_dir (str or Path) – Root directory for figures (e.g. “figures/process_colocalization”). Subdirs “pdf” and “png” are created inside it.
dt (datetime, optional) – Timestamp for version prefix; if None, uses now().
bbox_inches (str, optional) – Passed to savefig. Default “tight”.
pad_inches (float, optional) – Passed to savefig. Default 0.1.
create_pdf_png_folders (bool, optional) – If True (default), save under output_dir/pdf/ and output_dir/png/.
- Returns:
(path_to_pdf, path_to_png)
- Return type:
tuple of Path