Skip to main content
A heatmap renders a two-dimensional grid of colored cells where cell color encodes a numeric value. It is the standard visualization for correlation matrices, gene expression across samples, confusion matrices, and any rectangular data where rows and columns represent categories.

When to Use

  • Gene expression: Show expression levels across samples/conditions
  • Correlation matrices: Visualize pairwise correlations between variables
  • Confusion matrices: Display classification performance
  • Distance/similarity matrices: Reveal clustering structure
  • Contingency tables: Show counts across categorical dimensions
  • Any matrix data: When rows and columns have categorical labels

Basic Example

use kuva::plot::{Heatmap, ColorMap};
use kuva::backend::svg::SvgBackend;
use kuva::render::render::render_multiple;
use kuva::render::layout::Layout;
use kuva::render::plots::Plot;

let data = vec![
    vec![0.8, 0.3, 0.9],
    vec![0.4, 0.7, 0.1],
    vec![0.5, 0.9, 0.4],
];

let heatmap = Heatmap::new()
    .with_data(data)
    .with_color_map(ColorMap::Viridis);

let plots = vec![Plot::Heatmap(heatmap)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Heatmap");

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

Key Methods

with_data(data)

Set the grid data as a nested iterable. Outer iterator produces rows (top to bottom); inner iterator produces columns (left to right).
let heatmap = Heatmap::new().with_data(vec![
    vec![1.0, 2.0, 3.0],
    vec![4.0, 5.0, 6.0],
]);
All rows must have the same number of columns. Accepts any numeric type via Into<f64>.

with_labels(rows: Vec<String>, cols: Vec<String>)

Store row and column label strings in the struct. Important: These are not rendered automatically. To display them on the axes, pass them separately to Layout:
let row_labels = vec!["GeneA".into(), "GeneB".into(), "GeneC".into()];
let col_labels = vec!["Ctrl".into(), "T1".into(), "T2".into()];

let heatmap = Heatmap::new()
    .with_data(data)
    .with_labels(row_labels.clone(), col_labels.clone());

let plots = vec![Plot::Heatmap(heatmap)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Gene Expression")
    .with_x_categories(col_labels)
    .with_y_categories(row_labels);

with_color_map(map: ColorMap)

Set the color map applied after normalizing values to [0.0, 1.0]. Options:
  • ColorMap::Viridis (default) — Blue → green → yellow; perceptually uniform
  • ColorMap::Inferno — Black → purple → yellow; high contrast
  • ColorMap::Grayscale — Black → white; print-friendly
  • ColorMap::Custom(Arc<dyn Fn(f64) -> String>) — User-defined mapping
let heatmap = Heatmap::new()
    .with_data(data)
    .with_color_map(ColorMap::Inferno);

with_values()

Overlay numeric values inside each cell, formatted to two decimal places.
let heatmap = Heatmap::new()
    .with_data(vec![vec![10.0, 20.0], vec![30.0, 40.0]])
    .with_values();
Most useful for small grids where the text remains legible.

with_legend(label: impl Into<String>)

Attach a legend label to the heatmap.
let heatmap = Heatmap::new()
    .with_data(data)
    .with_legend("Expression (log2 TPM)");

Examples

Minimal Heatmap

let data = vec![
    vec![0.8, 0.3, 0.9, 0.2, 0.6],
    vec![0.4, 0.7, 0.1, 0.8, 0.3],
    vec![0.5, 0.9, 0.4, 0.6, 0.1],
    vec![0.2, 0.5, 0.8, 0.3, 0.7],
];

let heatmap = Heatmap::new().with_data(data);

let plots = vec![Plot::Heatmap(heatmap)];
let layout = Layout::auto_from_plots(&plots).with_title("Heatmap");
No axis labels — just the colored grid.

Gene Expression Heatmap

let data = vec![
    vec![2.1, 0.4, 3.2, 1.1, 2.8],
    vec![0.9, 3.5, 0.3, 2.7, 1.2],
    vec![1.8, 2.9, 1.5, 0.6, 3.1],
    vec![3.3, 1.1, 2.0, 3.8, 0.5],
];

let row_labels = ["GeneA", "GeneB", "GeneC", "GeneD"]
    .map(String::from).to_vec();
let col_labels = ["Ctrl", "T1", "T2", "T3", "T4"]
    .map(String::from).to_vec();

let heatmap = Heatmap::new().with_data(data);
let plots = vec![Plot::Heatmap(heatmap)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Gene Expression Heatmap")
    .with_x_categories(col_labels)
    .with_y_categories(row_labels);
Row labels (genes) on the y-axis; column labels (conditions) on the x-axis.

Value Overlay

let data = vec![
    vec![10.0, 20.0, 30.0, 15.0],
    vec![45.0, 55.0, 25.0, 60.0],
    vec![70.0, 35.0, 80.0, 40.0],
    vec![50.0, 90.0, 65.0, 20.0],
];

let heatmap = Heatmap::new()
    .with_data(data)
    .with_values();

let plots = vec![Plot::Heatmap(heatmap)];
let layout = Layout::auto_from_plots(&plots).with_title("Value Overlay");
Numeric values printed inside cells for precise reading.

Colormap Comparison

for (cmap, name, title) in [
    (ColorMap::Viridis,   "viridis",   "Viridis"),
    (ColorMap::Inferno,   "inferno",   "Inferno"),
    (ColorMap::Grayscale, "greyscale", "Greyscale"),
] {
    let heatmap = Heatmap::new()
        .with_data(data.clone())
        .with_color_map(cmap);

    let plots = vec![Plot::Heatmap(heatmap)];
    let layout = Layout::auto_from_plots(&plots).with_title(title);

    let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
    std::fs::write(format!("heatmap_{name}.svg"), svg).unwrap();
}
Viridis for general use, Inferno for high contrast, Grayscale for print.

Colorbar

A colorbar is always shown in the right margin, mapping the data range to the active color map.

See Also

  • Histogram2D — For binned scatter data instead of pre-computed grids
  • DotPlot — For encoding two variables (size and color) in a categorical grid

Build docs developers (and LLMs) love