Skip to main content

stanhue logo

stanhue

Hierarchical auto-coloring for scatter plots with many categorical labels.
One function. Two inputs. Publication-quality palettes.

PythonRAlgorithmCustom Palette


Gallery

All plots below are generated fully automatically — no manual color picking.

PBMC CITE-seq (57 cell types) Brain (21 cell types, 2.5M cells)
Heart (17 cell types) Meniscus (15 cell types)
Brain — 382 clusters, 83 auto-groups (click to expand)

Why?

When a scatter plot has dozens of categories (e.g., 30+ cell types on a UMAP), picking colors by hand is tedious and the result is usually ugly. Random palettes scatter similar hues across unrelated groups; sequential palettes make neighbors indistinguishable.

stanhue solves this by inferring group structure from the 2D layout itself, then assigning colors so that:

  • Distant groups get different hue families (blue vs red vs green)
  • Related categories get adjacent shades within the same family
  • Dominant categories (most points) anchor each group's representative color
  • Overlapping categories (points mixed together in 2D) get perceptually distant colors, even if they belong to the same group

It returns a simple {label: hex_color} mapping. You bring your own plotting code.

Quick Start

Python

from stanhue import assign_celltype_colors

colors = assign_celltype_colors(coords, labels)
# {"CD4 Naive": "#a6cee3", "CD8 TEM": "#1f78b4", ...}

R

source("scatter_colormap.R")

colors <- assign_celltype_colors(coords, labels)
# c("CD4 Naive" = "#a6cee3", "CD8 TEM" = "#1f78b4", ...)

Installation

pip (Python)

pip install git+https://github.com/chansigit/stanhue.git

Then from stanhue import assign_celltype_colors.

Script (no package manager needed)

Just copy the script you need:

Language File Dependencies
Python stanhue/scatter_colormap.py (standalone, no package needed) numpy, scipy
R scatter_colormap.R base R only (stats)
# Python
pip install numpy scipy

# R — no extra packages needed for core functionality

Claude Code plugin

/plugins add chansigit/stanhue

Once installed, mention "stanhue" to Claude and the skill activates automatically.

Python API

from stanhue import assign_celltype_colors, get_groups

# Basic usage
color_map = assign_celltype_colors(
    coords,              # (n, 2) array — any 2D embedding
    labels,              # (n,) array — categorical labels
    n_major_groups=None,  # auto-detect, or set manually
    palette=None,         # default: PAIRED_PALETTE (12 colors)
    overlap_aware=True,   # overlapping categories get distant colors
    overlap_threshold=0.1,
)

# Inspect grouping structure
groups = get_groups(coords, labels)
# {1: ["CD4 Naive", "CD4 TCM", ...], 2: ["CD8 TEM", ...], ...}

Seurat / SCE convenience (R)

# Seurat
colors <- color_seurat(seurat_obj, reduction = "umap", group_by = "cell_type")

# SingleCellExperiment
colors <- color_sce(sce_obj, dimred = "UMAP", col_name = "cell_type")

Algorithm

flowchart TD
    A["🔢 Input: 2D coords + labels"] --> B["1️⃣ Compute centroid per category"]
    B --> C["2️⃣ Ward hierarchical clustering on centroids"]
    C --> D{"Auto-determine k?"}
    D -- "Yes" --> E["Scan dendrogram gaps from<br/>large k → small k, pick first<br/>significant jump (≥ 2× median)"]
    D -- "No (user-specified)" --> F["Use provided n_major_groups"]
    E --> G["3️⃣ Cut dendrogram into k groups"]
    F --> G
    G --> H["4️⃣ Order within each group:<br/>• dominant (most cells) → position 0<br/>• rest by dendrogram leaf order"]
    H --> I["5️⃣ Sort groups by total cell count (descending)<br/>assign palette offsets with step = 2"]
    I --> J{"n_groups > palette_size / 2?"}
    J -- "No" --> K["Offsets: 0, 2, 4, 6, 8, 10"]
    J -- "Yes" --> L["Interleave: evens first,<br/>then odds, then cycle"]
    K --> M["6️⃣ Walk palette from each<br/>group's offset (mod palette_len)"]
    L --> M
    M --> O["7️⃣ Overlap-aware refinement:<br/>grid-based label mixing → overlap matrix<br/>dominant keeps anchor; overlapping members<br/>pick max CIELAB ΔE slot within the group"]
    O --> N["✅ Output: { label: '#hex' }"]

    style A fill:#f0f0f0,stroke:#333
    style N fill:#d4edda,stroke:#28a745

The algorithm is deterministic — same input always produces the same colors.

Custom Palette

The default is ColorBrewer Paired (12 colors, 6 light/dark pairs). Pass any ordered list of hex colors to override:

warm = ["#fee5d9", "#fcbba1", "#fc9272", "#fb6a4a", "#de2d26", "#a50f15"]
colors = assign_celltype_colors(coords, labels, palette=warm)

The offset logic adapts automatically.

Parameters

Parameter Default Description
coords required 2D coordinates, shape (n, 2)
labels required Categorical labels, length n
n_major_groups auto Number of top-level groups. None = auto-detect via relative gap
palette PAIRED_PALETTE Ordered hex color list of any length
overlap_aware True Categories that overlap in 2D get perceptually distant colors (CIELAB ΔE). Non-overlapping categories keep the hierarchical scheme. False = legacy behavior
overlap_threshold 0.1 Spatial mixing score (0–1) below which two categories are treated as non-overlapping

Tips

  • Two clusters share a color? Increase n_major_groups.
  • Related categories got unrelated colors? Decrease n_major_groups.
  • Two overlapping categories still look alike? Lower overlap_threshold (e.g. 0.05). Colors within a lineage look scrambled? Raise it, or set overlap_aware=False.
  • Few categories (≤ palette size)? Colors are handed out in farthest-point order in CIELAB, so 2 categories get two strongly contrasting hues rather than light/dark of the same hue.
  • 30+ categories? Consider a larger palette (e.g., 20 colors).
  • Works with any 2D embedding: UMAP, tSNE, PCA, PHATE, etc.
  • Not limited to single-cell data — any scatter plot with categorical labels.

Input Validation

Both implementations validate inputs and raise clear errors:

  • coords must be (n, 2) numeric without NaN/Inf
  • labels must match coords row count
  • At least 1 unique label required

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stanhue-1.1.0.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stanhue-1.1.0-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file stanhue-1.1.0.tar.gz.

File metadata

  • Download URL: stanhue-1.1.0.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stanhue-1.1.0.tar.gz
Algorithm Hash digest
SHA256 6a2f85660d29b76825f4dc68065d7ac501ddf010027dbc3544ffbe46baaf1df7
MD5 8535f9fae2fcde2bb8f1b6d85b1d5a7a
BLAKE2b-256 3d8d8acc4db273b39214d0c702962e5ed77004af1be7210e811511ffe0d0f238

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanhue-1.1.0.tar.gz:

Publisher: publish.yml on chansigit/stanhue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stanhue-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: stanhue-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stanhue-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 40039e516e61195bdae5681236cb7994aa4da5fb68890faf8c42650d99c57f99
MD5 97f7d9101afa4c252465576fb71782f8
BLAKE2b-256 a69d9f659e98699c36af417ed0aa46609c5a17a9cf93b98ccb6475fd44009e21

See more details on using hashes here.

Provenance

The following attestation bundles were made for stanhue-1.1.0-py3-none-any.whl:

Publisher: publish.yml on chansigit/stanhue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.1.1

2 files

This release

1.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page