Parallel Coordinates Plot
A parallel coordinates plot displays multivariate data by drawing one vertical axis per dimension and connecting each observation as a polyline that passes through its value on each axis. Groups of observations that share a similar pattern appear as bundles of lines with similar trajectories; divergent groups cross each other clearly.
Parallel coordinates are useful for exploring high-dimensional datasets, comparing groups across many measured attributes, and identifying which dimensions best separate groups.
Import path: kuva::plot::parallel::{ParallelPlot, ParallelRow}
Basic usage
Set axis names with .with_axis_names(), then add rows with .with_row_group(group, values). Each row is one observation; values must be in the same order as the axis names.
#![allow(unused)] fn main() { use kuva::plot::parallel::ParallelPlot; use kuva::backend::svg::SvgBackend; use kuva::render::render::render_multiple; use kuva::render::layout::Layout; use kuva::render::plots::Plot; let plot = ParallelPlot::new() .with_axis_names(["Sepal L", "Sepal W", "Petal L", "Petal W"]) .with_row_group("setosa", vec![5.1, 3.5, 1.4, 0.2]) .with_row_group("setosa", vec![4.9, 3.0, 1.4, 0.2]) .with_row_group("setosa", vec![4.7, 3.2, 1.3, 0.2]) .with_row_group("versicolor", vec![7.0, 3.2, 4.7, 1.4]) .with_row_group("versicolor", vec![6.4, 3.2, 4.5, 1.5]) .with_row_group("versicolor", vec![6.9, 3.1, 4.9, 1.5]) .with_row_group("virginica", vec![6.3, 3.3, 6.0, 2.5]) .with_row_group("virginica", vec![5.8, 2.7, 5.1, 1.9]) .with_row_group("virginica", vec![7.1, 3.0, 5.9, 2.1]) .with_legend("Species"); let plots = vec![Plot::Parallel(plot)]; let layout = Layout::auto_from_plots(&plots) .with_title("Iris Dataset"); let svg = SvgBackend.render_scene(&render_multiple(plots, layout)); std::fs::write("parallel.svg", svg).unwrap(); }
Each axis is normalised to [0, 1] by default so that differently-scaled dimensions are comparable. Disable with .with_normalize(false) when all axes share a common unit.
Smooth curves
.with_curved(true) draws S-shaped cubic Bézier curves instead of straight polylines, which reduces visual clutter in dense plots.
#![allow(unused)] fn main() { use kuva::plot::parallel::ParallelPlot; use kuva::render::plots::Plot; use kuva::render::layout::Layout; use kuva::render::render::render_multiple; let plot = ParallelPlot::new() .with_axis_names(["Sepal L", "Sepal W", "Petal L", "Petal W"]) .with_row_group("setosa", vec![5.1, 3.5, 1.4, 0.2]) .with_row_group("setosa", vec![4.9, 3.0, 1.4, 0.2]) .with_row_group("versicolor", vec![7.0, 3.2, 4.7, 1.4]) .with_row_group("versicolor", vec![6.4, 3.2, 4.5, 1.5]) .with_row_group("virginica", vec![6.3, 3.3, 6.0, 2.5]) .with_row_group("virginica", vec![7.1, 3.0, 5.9, 2.1]) .with_curved(true) .with_opacity(0.7) .with_legend("Species"); let plots = vec![Plot::Parallel(plot)]; }
Group mean overlay
.with_mean(true) draws a bold polyline at the per-group mean for each axis. This makes the group-level pattern visible even when individual lines are dense.
#![allow(unused)] fn main() { use kuva::plot::parallel::ParallelPlot; use kuva::render::plots::Plot; use kuva::render::layout::Layout; use kuva::render::render::render_multiple; use kuva::backend::svg::SvgBackend; let plot = ParallelPlot::new() .with_axis_names(["Recall", "Precision", "F1", "AUC", "Inference ms"]) // Multiple runs per model .with_group_rows("BERT", [ vec![0.82, 0.85, 0.83, 0.91, 120.0], vec![0.80, 0.87, 0.83, 0.90, 115.0], vec![0.83, 0.84, 0.83, 0.92, 125.0], ]) .with_group_rows("DistilBERT", [ vec![0.78, 0.80, 0.79, 0.87, 55.0], vec![0.77, 0.82, 0.79, 0.86, 52.0], vec![0.79, 0.81, 0.80, 0.88, 58.0], ]) .with_group_rows("LSTM", [ vec![0.72, 0.75, 0.73, 0.82, 30.0], vec![0.71, 0.76, 0.73, 0.81, 28.0], vec![0.73, 0.74, 0.73, 0.83, 32.0], ]) .with_mean(true) .with_opacity(0.35) .with_curved(true) .with_legend("Model"); let plots = vec![Plot::Parallel(plot)]; let layout = Layout::auto_from_plots(&plots).with_title("NLP Model Comparison"); let svg = SvgBackend.render_scene(&render_multiple(plots, layout)); }
Axis inversion
Some axes are naturally "better when low" (e.g., error rate, latency). .with_inverted_axis(i) inverts axis i so that high values plot near the bottom — a visual triangle at the bottom of the axis label indicates inversion.
#![allow(unused)] fn main() { use kuva::plot::parallel::ParallelPlot; use kuva::render::plots::Plot; use kuva::render::layout::Layout; use kuva::render::render::render_multiple; // Axes: Accuracy (higher = better), Error rate (lower = better), Speed ms (lower = better), F1 (higher = better) let plot = ParallelPlot::new() .with_axis_names(["Accuracy", "Error rate", "Speed (ms)", "F1"]) .with_row_group("Model A", vec![0.92, 0.08, 120.0, 0.90]) .with_row_group("Model A", vec![0.91, 0.09, 115.0, 0.89]) .with_row_group("Model B", vec![0.87, 0.13, 45.0, 0.86]) .with_row_group("Model B", vec![0.88, 0.12, 48.0, 0.87]) .with_inverted_axes([1, 2]) // invert Error rate and Speed: down = better .with_mean(true) .with_legend("Model"); let plots = vec![Plot::Parallel(plot)]; }
ParallelPlot API reference
ParallelPlot builders
| Method | Default | Description |
|---|---|---|
ParallelPlot::new() | — | Create a parallel coordinates plot |
.with_axis_names(iter) | — | Set axis (column) names |
.with_row(values) | — | Add an ungrouped row |
.with_row_group(group, values) | — | Add a row assigned to a named group |
.with_rows(iter) | — | Add multiple ungrouped rows |
.with_group_rows(group, iter) | — | Add multiple rows to the same group |
.with_normalize(bool) | true | Normalise each axis independently to [0, 1] |
.with_curved(bool) | false | Draw smooth S-shaped Bézier curves |
.with_stroke_width(px) | 1.2 | Polyline stroke width |
.with_opacity(f) | 0.6 | Polyline opacity |
.with_color(css) | "steelblue" | Fallback color for ungrouped rows |
.with_group_colors(iter) | palette | Explicit per-group CSS colors |
.with_mean(bool) | false | Draw a bold mean line for each group |
.with_inverted_axis(i) | — | Invert axis i (high values at bottom) |
.with_inverted_axes(iter) | — | Invert multiple axes |
.with_legend(label) | — | Legend title (one entry per group) |