Skip to main content
A band plot fills the region between two y-curves over a shared x-axis. It is typically used to display confidence intervals, prediction bands, interquartile ranges, or any uncertainty envelope around a data series.

When to Use

  • Confidence intervals: Show 95% CI around a regression line
  • Prediction bands: Display forecast uncertainty
  • IQR envelopes: Visualize quartile ranges in time series
  • Error bars: Show measurement uncertainty along a curve
  • Range visualization: Display min/max bounds for grouped data

Basic Example

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

let x: Vec<f64> = (0..50).map(|i| i as f64 * 0.2).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
let lower: Vec<f64> = y.iter().map(|&v| v - 0.3).collect();
let upper: Vec<f64> = y.iter().map(|&v| v + 0.3).collect();

let band = BandPlot::new(x.clone(), lower, upper)
    .with_color("steelblue")
    .with_opacity(0.25);

let line = LinePlot::new()
    .with_data(x.iter().copied().zip(y.iter().copied()))
    .with_color("steelblue");

let plots = vec![Plot::Band(band), Plot::Line(line)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Confidence Band")
    .with_x_label("x")
    .with_y_label("y");

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

Usage Modes

Standalone Mode

Create with BandPlot::new and add as Plot::Band. Pair with a Plot::Line or Plot::Scatter in the same plots vector to draw the band behind the data series.
let band = BandPlot::new(x.clone(), lower, upper)
    .with_color("steelblue")
    .with_opacity(0.25);

let line = LinePlot::new()
    .with_data(x.iter().copied().zip(y.iter().copied()))
    .with_color("steelblue");

let plots = vec![Plot::Band(band), Plot::Line(line)];

Attached Mode

Use LinePlot::with_band or ScatterPlot::with_band as a one-call shorthand. The band inherits the series color automatically.
let line = LinePlot::new()
    .with_data(x.iter().copied().zip(y.iter().copied()))
    .with_color("firebrick")
    .with_band(lower, upper);

let plots = vec![Plot::Line(line)];

Key Methods

BandPlot::new(x, y_lower, y_upper)

Create a band from parallel x, lower-bound, and upper-bound iterables. All three must have the same length.
let x = vec![0.0_f64, 1.0, 2.0, 3.0];
let lower = vec![-0.3_f64, 0.7, 1.7, 2.7];
let upper = vec![ 0.3_f64, 1.3, 2.3, 3.3];

let band = BandPlot::new(x, lower, upper);
Default fill: "steelblue" at opacity 0.2.

with_color(color: impl Into<String>)

Set the fill color (default "steelblue"). Accepts any CSS color string.
let band = BandPlot::new(x, lower, upper)
    .with_color("seagreen");
When using standalone mode, match the paired line or scatter color for a cohesive look.

with_opacity(opacity: f64)

Set the fill opacity in [0.0, 1.0] (default 0.2). Lower values make the band more transparent.
let band = BandPlot::new(x, lower, upper)
    .with_opacity(0.3);

with_legend(label: impl Into<String>)

Enable a legend entry with a filled rectangle swatch.
let band = BandPlot::new(x, lower, upper)
    .with_legend("95% CI");

Examples

Attached to Line

let x: Vec<f64> = (0..80).map(|i| i as f64 * std::f64::consts::PI / 20.0).collect();
let y: Vec<f64> = x.iter().map(|&v| (-v * 0.15).exp() * v.cos()).collect();
let lower: Vec<f64> = y.iter().map(|&v| v - 0.2).collect();
let upper: Vec<f64> = y.iter().map(|&v| v + 0.2).collect();

let line = LinePlot::new()
    .with_data(x.iter().copied().zip(y.iter().copied()))
    .with_color("firebrick")
    .with_band(lower, upper);

let plots = vec![Plot::Line(line)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Band Attached to Line")
    .with_x_label("x")
    .with_y_label("e^(−0.15x) · cos(x)");
.with_band() creates a band using the line’s own x positions and inherits the color automatically.

Attached to Scatter

let x: Vec<f64> = (0..30).map(|i| i as f64).collect();
let y: Vec<f64> = x.iter().map(|&v| 0.4 * v + 2.0).collect();
let lower: Vec<f64> = y.iter().map(|&v| v - 2.5).collect();
let upper: Vec<f64> = y.iter().map(|&v| v + 2.5).collect();

let scatter = ScatterPlot::new()
    .with_data(x.iter().copied().zip(y.iter().copied()))
    .with_color("seagreen")
    .with_band(lower, upper);

let plots = vec![Plot::Scatter(scatter)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Band Attached to Scatter")
    .with_x_label("x")
    .with_y_label("y");
Same pattern as LinePlot::with_band: band inherits scatter color and is drawn behind points.

Multiple Series with Bands

let x: Vec<f64> = (0..60).map(|i| i as f64 * 0.2).collect();

let y1: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
let y2: Vec<f64> = x.iter().map(|&v| (v * 0.5).cos() * 0.8).collect();

let line1 = LinePlot::new()
    .with_data(x.iter().copied().zip(y1.iter().copied()))
    .with_color("steelblue")
    .with_band(
        y1.iter().map(|&v| v - 0.25),
        y1.iter().map(|&v| v + 0.25),
    )
    .with_legend("sin(x)");

let line2 = LinePlot::new()
    .with_data(x.iter().copied().zip(y2.iter().copied()))
    .with_color("darkorange")
    .with_band(
        y2.iter().map(|&v| v - 0.25),
        y2.iter().map(|&v| v + 0.25),
    )
    .with_legend("0.8 · cos(0.5x)");

let plots = vec![Plot::Line(line1), Plot::Line(line2)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Multiple Series with Bands")
    .with_x_label("x")
    .with_y_label("y");
Each line carries its own independent band. All four plot elements share the same axes.

See Also

Build docs developers (and LLMs) love