Lollipop Chart

A lollipop chart displays discrete data points as vertical stems (lines) topped with filled circles. It conveys the same information as a bar chart while being less visually heavy — the empty space between stems makes it easier to compare the heights of nearby points.

A distinctive feature of lollipop charts is the optional domain annotation: colored rectangles drawn behind the stems along the x-axis. This makes them the standard format for mutation landscape plots in molecular biology, where each lollipop shows a mutation count and colored bands below the axis indicate protein functional domains.

Import path: kuva::plot::lollipop::LollipopPlot


Basic usage

Add points with .with_point(x, y). All stems originate from the baseline (y = 0 by default).

#![allow(unused)]
fn main() {
use kuva::plot::lollipop::LollipopPlot;
use kuva::backend::svg::SvgBackend;
use kuva::render::render::render_multiple;
use kuva::render::layout::Layout;
use kuva::render::plots::Plot;

let plot = LollipopPlot::new()
    .with_point(1.0,  4.0)
    .with_point(3.0,  7.0)
    .with_point(5.0,  3.0)
    .with_point(7.0,  9.0)
    .with_point(9.0,  5.0)
    .with_point(11.0, 2.0)
    .with_point(13.0, 8.0)
    .with_color("steelblue");

let plots = vec![Plot::Lollipop(plot)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Gene Expression Rank")
    .with_x_label("Gene index")
    .with_y_label("Expression (log₂ TPM)");

let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
std::fs::write("lollipop.svg", svg).unwrap();
}
Basic lollipop chart

Labels and per-point colors

.with_labeled_point() places a text label above (or below, for negative values) the dot. .with_colored_point() and .with_labeled_colored_point() allow per-point color overrides for highlighting specific items.

#![allow(unused)]
fn main() {
use kuva::plot::lollipop::LollipopPlot;
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;

let plot = LollipopPlot::new()
    .with_point(5.0, 2.0)
    .with_point(15.0, 3.0)
    .with_labeled_colored_point(28.0, 12.0, "TP53",  "#d62728")
    .with_point(35.0, 4.0)
    .with_labeled_colored_point(47.0,  9.0, "KRAS",  "#ff7f0e")
    .with_point(62.0, 2.0)
    .with_labeled_colored_point(75.0, 14.0, "BRCA1", "#9467bd")
    .with_point(88.0, 3.0)
    .with_dot_radius(5.5);

let plots = vec![Plot::Lollipop(plot)];
}
Lollipop chart with labeled and colored data points

Mutation landscape with domain annotations

.with_domain() draws a colored rectangle behind the stems, anchored below the baseline. This is the standard presentation for protein mutation landscapes, where domains (functional regions) are annotated along the sequence.

#![allow(unused)]
fn main() {
use kuva::plot::lollipop::LollipopPlot;
use kuva::backend::svg::SvgBackend;
use kuva::render::render::render_multiple;
use kuva::render::layout::Layout;
use kuva::render::plots::Plot;

let plot = LollipopPlot::new()
    // Missense mutations
    .with_labeled_colored_point( 12.0, 3.0, "R12H",   "#1f77b4")
    .with_labeled_colored_point( 35.0, 8.0, "G35V",   "#d62728")
    .with_labeled_colored_point( 67.0, 2.0, "K67R",   "#1f77b4")
    .with_labeled_colored_point( 94.0, 5.0, "P94L",   "#d62728")
    .with_labeled_colored_point(118.0, 11.0, "R118*", "#d62728")
    .with_labeled_colored_point(145.0, 4.0, "T145A",  "#1f77b4")
    .with_labeled_colored_point(173.0, 7.0, "D173N",  "#d62728")
    .with_labeled_colored_point(201.0, 3.0, "E201K",  "#1f77b4")
    // Domain annotations
    .with_domain(  1.0,  55.0, Some("N-term"),   "#4e79a7")
    .with_domain( 56.0, 130.0, Some("Kinase"),   "#f28e2b")
    .with_domain(131.0, 195.0, Some("SH2"),      "#59a14f")
    .with_domain(196.0, 240.0, Some("C-term"),   "#b07aa1")
    .with_domain_height(0.8)
    .with_stem_width(1.5)
    .with_dot_radius(5.0);

let plots = vec![Plot::Lollipop(plot)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("TP53 Mutation Landscape")
    .with_x_label("Amino acid position")
    .with_y_label("Count");

let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
}
Mutation landscape with protein domain annotations

Negative values and custom baseline

The baseline defaults to y = 0. Set a different baseline with .with_baseline(). Points below the baseline have their stems drawn downward and labels placed below the dot.

#![allow(unused)]
fn main() {
use kuva::plot::lollipop::LollipopPlot;
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;

// Log2 fold-change: positive = upregulated, negative = downregulated
let plot = LollipopPlot::new()
    .with_point( 1.0,  2.3)
    .with_point( 2.0, -1.8)
    .with_point( 3.0,  0.5)
    .with_point( 4.0, -3.1)
    .with_point( 5.0,  1.9)
    .with_point( 6.0, -0.7)
    .with_point( 7.0,  4.2)
    .with_baseline(0.0)
    .with_baseline_dash("4 3");

let plots = vec![Plot::Lollipop(plot)];
}

LollipopPlot API reference

LollipopPlot builders

MethodDefaultDescription
LollipopPlot::new()Create a lollipop chart with default settings
.with_point(x, y)Add a point with no label
.with_labeled_point(x, y, label)Add a point with a text label
.with_colored_point(x, y, color)Add a point with a per-point color override
.with_labeled_colored_point(x, y, label, color)Add a labeled point with a per-point color
.with_points(iter)Add multiple (x, y) pairs at once
.with_color(css)"steelblue"Default stem and dot color
.with_baseline(v)0.0Y value where stems originate
.with_stem_width(px)1.5Stem stroke width
.with_dot_radius(px)5.0Dot radius
.with_dot_stroke(css)fill colorDot outline color
.with_dot_stroke_width(px)1.0Dot outline width
.with_show_baseline(bool)trueDraw a horizontal baseline line
.with_baseline_color(css)"#888888"Baseline line color
.with_baseline_width(px)1.0Baseline line stroke width
.with_baseline_dash(s)Baseline dasharray (e.g. "4 3")
.with_domain(x0, x1, label, color)Add a domain annotation rect
.with_domain_opacity(x0, x1, label, color, opacity)Domain rect with explicit opacity
.with_domain_height(h)0.5Domain rect height in data units below the baseline
.with_legend(label)Attach a legend (colored circle entry)