| Title: | Hierarchy-Aware, Interactively Editable Color Palettes for Clusters |
| Version: | 1.0.1 |
| Description: | Assigns perceptually-informed color palettes to clustered data (e.g. scRNA-seq clusters / cell subtypes from 'Seurat' or plain UMAP coordinate tables), where clusters that belong to the same "family" (e.g. subtypes of T cells) can be colored either harmoniously (shared hue, varying shade) or with maximal contrast (with family membership encoded via a secondary channel). Families can be auto-detected from embedding-space hierarchical clustering or supplied explicitly, and every generated color remains individually overridable, with manual overrides preserved across regeneration. Colors are constructed in the HCL space as in Zeileis et al. (2020) <doi:10.18637/jss.v096.i01>, separated using the CIEDE2000 color difference of Sharma, Wu and Dalal (2005) <doi:10.1002/col.20070>, and checked for color-vision deficiency using the simulation of Machado, Oliveira and Fernandes (2009) <doi:10.1109/TVCG.2009.113>. Core algorithms have no UI dependencies; an optional interactive 'shiny' UI is provided separately. |
| License: | Apache License (== 2.0) |
| Encoding: | UTF-8 |
| RoxygenNote: | 7.3.2 |
| Depends: | R (≥ 4.1.0) |
| Imports: | colorspace, farver, stats, graphics, utils, jsonlite |
| Suggests: | knitr, rmarkdown, testthat (≥ 3.0.0), ggplot2, Seurat, shiny, plotly, colourpicker, shinyjs, dendextend |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| URL: | https://github.com/thisishcb/Palettome |
| BugReports: | https://github.com/thisishcb/Palettome/issues |
| NeedsCompilation: | no |
| Packaged: | 2026-10-08 21:39:07 UTC; nigel |
| Author: | Chenbin Huang |
| Maintainer: | Chenbin Huang <chenbin.huang@emory.edu> |
| Repository: | CRAN |
| Date/Publication: | 2026-10-20 18:20:10 UTC |
palettome: Hierarchy-Aware, Interactively Editable Color Palettes
Description
Core functions (as_cluster_data(), detect_families(),
generate_palette(), plot_palette_static(), export_palette_json(),
...) depend only on colorspace/farver/stats/jsonlite and run with zero UI
packages installed. The optional interactive UI (launch_palettome_ui())
lives entirely behind Suggests-only packages (shiny, plotly, colourpicker,
shinyjs) and degrades to a friendly error if they are missing.
Author(s)
Maintainer: Chenbin Huang chenbin.huang@emory.edu (ORCID)
See Also
Useful links:
Placement of an analogous hue arc that avoids the muddy zone
Description
Probes arc start positions across hue_range, scores each by the worst
(not average) muddiness anywhere along the arc it would span – a mean
score can hide a single genuinely olive stretch, which is exactly the
part of the sweep that tends to land at low lightness where it's most
visible – then draws the returned start uniformly at random (by seed)
from every position that clears the tolerance, not just the single best
one. This is what keeps the harmonious "sweep" out of olive while still
giving different seeds genuinely different gradients (reds, teals,
greens, blues, purples, ...), rather than every seed converging on a
small jitter around one "optimal" blue-to-pink arc.
Usage
.best_arc_start(
arc,
hue_range = c(0, 360),
seed = 1,
n_probe = 72,
tolerance = 0.25,
jitter = 2.5
)
Arguments
arc |
Arc width in degrees. |
hue_range |
Two-element range the start may fall in. |
seed |
Seed controlling which acceptable start is picked, and the jitter applied to it. |
n_probe |
Number of start positions to test. |
tolerance |
Worst-point muddiness (0-1) a start may have and still be considered acceptable; widened automatically if nothing clears it. |
jitter |
Max +/- continuous jitter (degrees) added after picking a probe, so results aren't quantized to the probe grid. |
Value
A single hue in degrees.
Circular distance between hues, in degrees (0-180)
Description
Circular distance between hues, in degrees (0-180)
Usage
.circ_dist(a, b)
Cluster-by-cluster distance matrix from a neighbors argument
Description
Cluster-by-cluster distance matrix from a neighbors argument
Usage
.cluster_distance(neighbors, clusters)
Arguments
neighbors |
One of: |
clusters |
Character vector of cluster ids to restrict/order to. |
Value
A clusters x clusters numeric distance matrix (smaller = more
adjacent), or NULL if neighbors is NULL.
Greedy max-min ("Glasbey-style") color selection
Description
Greedily picks, from a candidate pool, the color that maximizes the
minimum perceptual distance to colors already chosen (including any
fixed/manual colors passed in), repeated until n colors are chosen.
Usage
.contrast_colors(
n,
seed,
lightness_range,
chroma_range,
hue_range = c(0, 360),
fixed_hex = character(0),
cvd = "none"
)
Arguments
n |
Number of new colors to choose. |
seed |
Random seed for candidate pool generation. |
lightness_range, chroma_range, hue_range |
Ranges the candidate pool is drawn from. |
fixed_hex |
Colors already in use (manual overrides); selection avoids clashing with these but does not return them. |
cvd |
|
Value
Character vector of n hex colors.
Family-by-family distance (single-linkage over cluster distances)
Description
Family-by-family distance (single-linkage over cluster distances)
Usage
.family_distance(cluster_dist, assignment)
Low-discrepancy hue sequence (golden-angle additive recurrence)
Description
Gives a well-spread set of hues for any n, so a "random" starting offset still produces a usable spread rather than occasional clumps.
Usage
.golden_hues(n, start = 0)
Build hex from HCL, clamping chroma into the displayable gamut
Description
chroma_frac keeps a margin below the true gamut boundary so colors
don't sit right on the edge (which fixup would otherwise distort).
Vectorised; H/C/L are recycled to a common length.
Usage
.hcl_hex(H, C, L, chroma_frac = 0.95)
How "muddy" a hue tends to look at mid/low lightness (0-1)
Description
The yellow / yellow-green arc (HCL hue ~60-115) turns olive and sickly when it isn't near-white and is hard to tell apart from its neighbors; a penalty there steers hue placement away from it when there is a free choice. Orange and pure green are only lightly touched.
Usage
.hue_muddiness_penalty(h)
Logical adjacency matrix: each item linked to its k nearest
Description
Logical adjacency matrix: each item linked to its k nearest
Usage
.knn_adjacency(d, k = NULL)
Natural perceived lightness of a fully-saturated hue
Description
Yellows and greens read light; blues and violets read dark. A multi-hue palette that lets lightness drift with hue in roughly this way looks more natural than one forced to constant lightness. Anchor points are approximate perceived lightnesses of vivid hues around the CIELUV wheel, linearly interpolated.
Usage
.natural_lightness(h)
Largest "pleasing" chroma for a hue at a lightness (gamut fraction, capped)
Description
Largest "pleasing" chroma for a hue at a lightness (gamut fraction, capped)
Usage
.pleasing_chroma(h, l, frac = 0.72, cap = 95)
1-D seriation order of items given a distance matrix
Description
Returns a permutation o such that o[j] is the item at position j
along a line that keeps near items near. Uses the first classical-MDS
coordinate, falling back to average-linkage dendrogram order.
Usage
.seriate_order(d)
Permutation of 1:n that puts consecutive inputs on far-apart slots
Description
Used so that items in spatial order (adjacent clusters, adjacent families) land on non-adjacent lightness levels or hue positions. For small n an exact max-min-gap permutation is found by brute force; for larger n a half-shift interleave (min gap approx n/2) is used.
Usage
.spread_slots(n, cyclic = FALSE)
Arguments
n |
Number of slots/items. |
cyclic |
If |
Value
Integer vector s of length n; item in position j (input
order) is assigned slot s[j].
Decode hex colors to an HCL matrix (columns h, c, l)
Description
Decode hex colors to an HCL matrix (columns h, c, l)
Usage
.to_hcl(hex)
Build a palettome data object from a data frame or Seurat object
Description
as_cluster_data() normalizes either a plain data frame of embedding
coordinates + cluster labels, or a Seurat object, into the common
palettome_data structure that every other core function (
detect_families(), generate_palette(), plot_palette_static(), ...)
consumes. This is the only place that needs to know about Seurat, and
Seurat is never required to be installed to use the rest of the package.
Usage
as_cluster_data(x, ...)
## S3 method for class 'data.frame'
as_cluster_data(
x,
coord_cols,
cluster_col,
family_col = NULL,
cell_id_col = NULL,
...
)
## S3 method for class 'Seurat'
as_cluster_data(
x,
reduction = "umap",
dims = 1:2,
cluster_col = "seurat_clusters",
family_col = NULL,
...
)
Arguments
x |
A |
... |
Passed to methods. |
coord_cols |
Character vector of 2 or 3 column names holding
embedding coordinates (e.g. |
cluster_col |
Name of the column holding cluster/subtype labels. |
family_col |
Optional name of a column holding an explicit
parent/family label. When |
cell_id_col |
Optional name of a column with unique per-row/cell
identifiers. When |
reduction |
Name of the dimensional reduction to pull coordinates
from (passed to |
dims |
Integer vector of length 2 or 3 selecting which dimensions of
|
Value
A palettome_data object: a list with elements
- cells
A data frame with columns
cell_id, the coordinate columns (as named incoord_cols),cluster, and optionallyfamily.- coord_cols
Character vector of coordinate column names in
cells, in order (length 2 or 3).- has_family
Logical; whether an explicit family column was supplied.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
str(pdata$cells)
# With an explicit family column
cells$lineage <- substr(cells$cluster, 1, 1)
pdata_fam <- as_cluster_data(cells, coord_cols = c("x", "y"),
cluster_col = "cluster", family_col = "lineage")
pdata_fam$has_family
Get cluster colors as a named vector or a plain data frame
Description
The most common thing to actually do with a palettome_session is hand
its colors to something else's plotting call (e.g. Seurat::DimPlot(cols = cluster_colors(session)), ggplot2::scale_color_manual(values = cluster_colors(session))). This pulls just the cluster -> color
mapping out of the fuller session object in whichever shape is
convenient, without needing to know the session's internal structure.
Usage
cluster_colors(session, format = c("vector", "data.frame"))
Arguments
session |
A |
format |
|
Value
A named character vector, or a data frame, depending on format.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
cluster_colors(session)
cluster_colors(session, format = "data.frame")
Pairwise perceptual color distance
Description
Thin wrapper over farver::compare_colour() in CIE Lab space, used both
by the contrast-mode selector and by the package tests to check that
generated colors respect a minimum perceptual separation.
Usage
color_distance(hex, method = "CIE2000")
Arguments
hex |
Character vector of hex colors. |
method |
Distance method passed to |
Value
A symmetric numeric matrix of pairwise distances, named by hex.
Examples
d <- color_distance(c("#1B9E77", "#D95F02", "#7570B3"))
round(d, 1)
Compute per-cluster centroids and cell counts
Description
Precomputes cluster centroids (mean coordinate per cluster), cell counts,
and (if present) the family each cluster belongs to. Every downstream
interactive operation (recoloring, regrouping) should be able to work off
this small summary rather than touching pdata$cells again.
Usage
compute_centroids(pdata)
Arguments
pdata |
A |
Value
A data frame with one row per cluster: cluster, one column per
coordinate named <coord>_centroid, n_cells, and family if
pdata$has_family is TRUE.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
compute_centroids(pdata)
Build a cluster-distance dendrogram for family detection
Description
Computes cluster centroids (or uses a supplied connectivity/distance
matrix) and runs hierarchical clustering on them. Kept separate from
detect_families() so a UI can render a dendrogram preview once and then
re-cut it at different k/h cheaply via cut_families(), without
recomputing distances every time the user drags a cut-height slider.
Usage
compute_family_dendrogram(
pdata,
dist_method = "euclidean",
hclust_method = "average",
connectivity = NULL
)
Arguments
pdata |
A |
dist_method |
Distance metric for centroid-based clustering, passed
to |
hclust_method |
Agglomeration method, passed to |
connectivity |
Optional square cluster-by-cluster connectivity or
similarity matrix (e.g. from a shared-nearest-neighbor graph), with row
and column names matching cluster ids. When supplied, it is converted to
a distance matrix ( |
Value
A list with hclust (an stats::hclust object), centroids
(from compute_centroids()), and clusters (the cluster id order used).
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
dendro <- compute_family_dendrogram(pdata)
dendro$hclust
Cut a precomputed dendrogram into families
Description
Cut a precomputed dendrogram into families
Usage
cut_families(dendro, k = NULL, h = NULL)
Arguments
dendro |
A list as returned by |
k |
Desired number of families (mutually exclusive with |
h |
Height at which to cut the dendrogram (mutually exclusive with
|
Value
A data frame with columns cluster and family_id (character,
"F1", "F2", ...).
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
dendro <- compute_family_dendrogram(pdata)
cut_families(dendro, k = 3)
Detect (or pass through) cluster families
Description
If pdata carries an explicit family column (i.e. pdata$has_family is
TRUE), that mapping is used as-is and no clustering is performed.
Otherwise, families are auto-detected by hierarchical clustering of
cluster centroids (or a supplied connectivity graph). Either way, the
result is meant to be a starting point: it is a plain
cluster -> family_id data frame that a UI can freely rewrite by hand
(drag-to-regroup) afterward.
Usage
detect_families(
pdata,
k = NULL,
h = NULL,
dist_method = "euclidean",
hclust_method = "average",
connectivity = NULL
)
Arguments
pdata |
A |
k, h |
See |
dist_method |
Distance metric for centroid-based clustering, passed
to |
hclust_method |
Agglomeration method, passed to |
connectivity |
Optional square cluster-by-cluster connectivity or
similarity matrix (e.g. from a shared-nearest-neighbor graph), with row
and column names matching cluster ids. When supplied, it is converted to
a distance matrix ( |
Value
A palettome_families object: a list with
- assignment
data frame with columns
cluster,family_id.- method
"explicit","hclust", or"graph".- dendro
The
compute_family_dendrogram()result, orNULLifmethod == "explicit"; kept so a UI can preview/re-cut the dendrogram without recomputation.- neighbors
Neighbor information ready to pass to
generate_palette()– the suppliedconnectivitymatrix if any, otherwise the cluster centroids – so neighbor-aware coloring works without recomputing anything.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
fam <- detect_families(pdata, k = 3)
fam$method
fam$assignment
Stratified per-cluster subsample for interactive preview
Description
Precomputing a small, cluster-stratified subsample is the core trick that
keeps hierarchy editing and recoloring responsive on multi-million-cell
datasets: interactive redraws use this subsample, and only a final export
needs to touch every cell. See inst/benchmark/ for a benchmark
justifying the approach and default threshold.
Usage
downsample_stratified(pdata, max_n = 50000, min_per_cluster = 20, seed = 1)
Arguments
pdata |
A |
max_n |
Target total number of points after subsampling. If
|
min_per_cluster |
Minimum points kept per cluster even if that over-represents small clusters relative to a uniform per-cluster share, so rare clusters stay visible. |
seed |
Random seed for reproducible subsampling. The caller's random number state is restored on exit. |
Value
A palettome_data object with a (possibly) reduced cells table.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
small <- downsample_stratified(pdata, max_n = 60, min_per_cluster = 5)
table(small$cells$cluster)
Export cluster and/or family color tables as CSV
Description
Export cluster and/or family color tables as CSV
Usage
export_palette_csv(session, clusters_file, families_file = NULL)
Arguments
session |
A |
clusters_file |
Path to write the cluster -> color table to (columns
|
families_file |
Optional path to also write the family -> color
table to (columns |
Value
Invisibly, TRUE.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
clusters_file <- file.path(tempdir(), "clusters.csv")
families_file <- file.path(tempdir(), "families.csv")
export_palette_csv(session, clusters_file, families_file)
read.csv(clusters_file)
unlink(c(clusters_file, families_file))
Export a palette session to JSON
Description
Writes the stable, language-agnostic JSON contract described in the
package README (schema_version, clusters, families, params) so a
future non-R (e.g. Python/Scanpy) frontend could read/write the same
file.
Usage
export_palette_json(session, file)
Arguments
session |
A |
file |
Path to write to. |
Value
Invisibly, the JSON string that was written.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
path <- file.path(tempdir(), "palette.json")
export_palette_json(session, path)
unlink(path)
Get family colors as a named vector or a plain data frame
Description
Same as cluster_colors(), but for each family's representative/accent
color (the harmonious-mode base hue, or the contrast-mode secondary
channel) rather than per-cluster fill colors.
Usage
family_colors(session, format = c("vector", "data.frame"))
Arguments
session |
A |
format |
|
Value
A named character vector, or a data frame, depending on format.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
family_colors(session)
family_colors(session, format = "data.frame")
Generate a cluster/family color palette
Description
The core color-generation entry point. Given a cluster -> family assignment, it places one base hue per family and then colors each cluster, in one of two modes:
Usage
generate_palette(
assignment,
session = NULL,
mode = c("harmonious", "contrast"),
harmonious_style = c("sweep", "per_family"),
sweep_anchors = NULL,
neighbor_hues = NULL,
neighbors = NULL,
seed = 1,
lightness_range = "auto",
chroma_range = "auto",
hue_range = c(0, 360),
cvd = c("none", "deutan", "protan", "tritan"),
n_cells = NULL
)
Arguments
assignment |
A data frame with columns |
session |
An existing |
mode |
|
harmonious_style |
|
sweep_anchors |
Optional character vector of 2+ hex colors (only
used when |
neighbor_hues |
|
neighbors |
Optional neighbor information used to order families and
clusters in space: a data frame of centroids ( |
seed |
Random seed, so the same inputs always give the same colors. The caller's random number state is restored on exit. |
lightness_range, chroma_range |
Length-2 numeric ranges in HCL space,
or |
hue_range |
Length-2 numeric range in degrees to draw hues from. |
cvd |
|
n_cells |
Optional named integer vector or |
Details
-
mode = "harmonious"– a coordinated palette in HCL space.harmonious_style = "sweep"(default) lays families along a single analogous hue arc with a shared monotone lightness ramp and muted chroma (a smooth blue -> purple -> rose -> tan style sweep);harmonious_style = "per_family"gives each family its own hue and colors its members as graded shades of it. -
mode = "contrast"– every cluster gets a maximally-distinguishable color via greedy max-min selection in Lab space; family membership is carried by$families$coloras a secondary channel.
Neighbor-awareness: when neighbors is supplied, families that are
adjacent in the embedding/graph get contrasting hues by default
(neighbor_hues = "contrast", the default in contrast mode) or
analogous hues (neighbor_hues = "coherent", the default in
harmonious mode); and within a family, spatially-adjacent clusters are
given far-apart lightness for local contrast.
Passing a previous session makes this incremental: any cluster or
family with manual_color == TRUE keeps its color, the generator treats
those as fixed and avoids clashing with them, and with
lightness_range/chroma_range left at "auto" the non-manual colors
are regenerated inside the L/C envelope implied by the manual picks.
Value
A palettome_session object (see the package README for the JSON
schema).
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
fam <- detect_families(pdata, k = 3)
# Harmonious: one coordinated hue sweep, neighbor-aware
session <- generate_palette(fam$assignment, mode = "harmonious",
neighbors = fam$neighbors, seed = 1)
cluster_colors(session)
# Different seeds give different sweeps
cluster_colors(generate_palette(fam$assignment, seed = 7))
# Contrast: every cluster maximally distinguishable
session_c <- generate_palette(fam$assignment, mode = "contrast", seed = 1)
cluster_colors(session_c)
# Sweep along a gradient you choose
cluster_colors(generate_palette(fam$assignment,
sweep_anchors = c("#0B3D91", "#FF5733")))
Import a palette session from JSON
Description
Import a palette session from JSON
Usage
import_palette_json(file)
Arguments
file |
Path to a JSON file written by |
Value
A palettome_session.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
path <- file.path(tempdir(), "palette.json")
export_palette_json(session, path)
session_back <- import_palette_json(path)
identical(cluster_colors(session_back), cluster_colors(session))
unlink(path)
Launch the interactive Palettome Shiny UI
Description
This is the only function in the package that requires UI packages
(shiny, plotly, colourpicker, shinyjs). All of them are Suggests-only, so
installing/loading the palettome package itself never requires them, and
every other function (detect_families(), generate_palette(),
plot_palette_static(), export_palette_json(), ...) works without
them. If any are missing, this fails with a friendly message rather than
an obscure error, and points out that the rest of the package still
works.
Usage
launch_palettome_ui(
pdata,
families = NULL,
session = NULL,
k = NULL,
h = NULL,
connectivity = NULL,
preview_max_n = 50000,
...
)
Arguments
pdata |
A |
families |
Optional: a |
session |
Optional: a |
k, h |
Passed to |
connectivity |
Optional cluster-by-cluster connectivity/distance
matrix passed to |
preview_max_n |
Passed to |
... |
Passed on to |
Value
Invisibly, the edited palettome_session once the app closes
(via the "Done" button, or by interrupting R with Esc / Ctrl+C), so
session <- launch_palettome_ui(...) keeps every change made in the
UI. The app also shows the current colors as pasteable named-vector R
code.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
if (interactive()) {
session <- launch_palettome_ui(pdata, k = 3)
cluster_colors(session)
}
Snap a hand-picked color into the palette's aesthetic envelope
Description
Feature companion to manual recoloring: keeps the hue you chose but pulls
its lightness and chroma to something in-gamut and coherent – either
matched to the lightness/chroma distribution of an existing session
(so a hand-picked color sits with the rest), or, with no session, toward
the natural lightness for that hue and a pleasing (non-neon) chroma.
Usage
optimize_color(color, session = NULL, target = c("auto", "muted", "vivid"))
Arguments
color |
A hex color string. |
session |
Optional |
target |
|
Value
A hex color string with the same hue, adjusted L/C.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
optimize_color("#7B00FF")
optimize_color("#7B00FF", session = session, target = "muted")
Plot a dendrogram preview of detected families
Description
A dependency-light preview: uses dendextend
to color branches by family when it is installed, and falls back to base
stats::plot.hclust() with stats::rect.hclust() otherwise. Either way
this only needs base R + stats to run.
Usage
plot_dendrogram(dendro, k = NULL, h = NULL)
Arguments
dendro |
A list from |
k, h |
Passed to |
Value
Invisibly, the (possibly colored) dendrogram object that was plotted.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
fam <- detect_families(pdata, k = 3)
plot_dendrogram(fam$dendro, k = 3)
Static plot of an embedding colored by the current palette
Description
Works with zero UI packages installed: uses ggplot2 for a nicer plot if
it happens to be available (it is Suggests-only, never required), and
otherwise falls back to base graphics::plot(). Either way, no Shiny/UI
dependency is ever touched.
Usage
plot_palette_static(
pdata,
session,
cvd = "none",
point_size = 0.6,
alpha = 0.7
)
Arguments
pdata |
A |
session |
A |
cvd |
|
point_size, alpha |
Passed through to the plotting backend. |
Details
For large datasets, consider subsampling pdata$cells (e.g. stratified
per cluster) before calling this for interactive-style previews, and only
plotting the full data for a final export; see inst/benchmark/ for the
rendering-strategy discussion behind this recommendation.
Value
A ggplot object if ggplot2 is installed, otherwise NULL
(invisibly) after drawing directly to the current base graphics device.
Examples
cells <- data.frame(
x = rnorm(120, rep(c(0, 1, 6, 7, 3, 4), each = 20)),
y = rnorm(120, rep(c(0, 1, 0, 1, 6, 7), each = 20)),
cluster = rep(c("T1", "T2", "B1", "B2", "M1", "M2"), each = 20)
)
pdata <- as_cluster_data(cells, coord_cols = c("x", "y"), cluster_col = "cluster")
fam <- detect_families(pdata, k = 3)
session <- generate_palette(fam$assignment, neighbors = fam$neighbors, seed = 1)
plot_palette_static(pdata, session)
Standalone swatch/legend figure, grouped by family
Description
A base-graphics-only (no ggplot2/Shiny needed) legend figure: one row per cluster, colored swatch + label, visually grouped and separated by family, with the family's own color shown as a header bar. Suitable for exporting as PNG/SVG alongside the main plot.
Usage
plot_swatches(session, cvd = "none", order_by = c("lightness", "id"))
Arguments
session |
A |
cvd |
|
order_by |
|
Value
Invisibly, NULL; draws to the current graphics device.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
plot_swatches(session)
plot_swatches(session, cvd = "deutan")
Reset manual color overrides
Description
Reset manual color overrides
Usage
reset_overrides(session, cluster = TRUE, family = TRUE)
Arguments
session |
A |
cluster, family |
Character vector of ids to reset, or |
Value
The updated palettome_session (colors are left as-is; only the
manual_color flags are cleared – call generate_palette() again to
recompute the now-unlocked colors).
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
session <- set_manual_color(session, cluster = "c1", color = "#2E5A87")
session <- reset_overrides(session)
any(session$clusters$manual_color)
Manually set a cluster's or family's color
Description
Sets color and flags manual_color = TRUE for the given cluster and/or
family in a session. Manually-colored entries are left untouched by later
generate_palette() calls (until reset with reset_overrides()).
Usage
set_manual_color(session, cluster = NULL, family = NULL, color)
Arguments
session |
A |
cluster, family |
Cluster ids / family ids to recolor (either or both; at least one required). May be vectors to recolor several at once. |
color |
Hex color string(s): either a single color applied to every
target, or one color per id (recycled separately for |
Value
The updated palettome_session.
Examples
assignment <- data.frame(cluster = paste0("c", 1:4), family_id = rep(c("F1", "F2"), each = 2))
session <- generate_palette(assignment, mode = "harmonious", seed = 1)
session <- set_manual_color(session, cluster = "c1", color = "#2E5A87")
session$clusters[, c("cluster", "color", "manual_color")]
# Several clusters at once, one color each
session <- set_manual_color(session, cluster = c("c3", "c4"),
color = c("#2E5A87", "#6E6E6E"))
# Manual picks survive regeneration
session <- generate_palette(assignment, session = session, mode = "contrast")
cluster_colors(session)[["c1"]]
Simulate color-vision deficiency on a palette
Description
Preview-only helper: does not mutate a session, just maps colors through
colorspace::deutan()/colorspace::protan()/colorspace::tritan() so a
UI can toggle a colorblind-safe preview of an already-generated palette.
Usage
simulate_cvd(hex, type = c("deutan", "protan", "tritan"), severity = 1)
Arguments
hex |
Character vector of hex colors. |
type |
One of |
severity |
Severity in |
Value
Character vector of simulated hex colors, same length as hex.
Examples
pal <- c("#1B9E77", "#D95F02", "#7570B3", "#E7298A")
simulate_cvd(pal, type = "deutan")
simulate_cvd(pal, type = "tritan", severity = 0.5)