4 releases (1 stable)

1.0.0 Jun 16, 2026
0.6.0 Jun 15, 2026
0.5.0 Jun 6, 2026
0.4.0 Jun 6, 2026

#299 in Visualization


Used in plotkit

MIT/Apache

700KB
15K SLoC

ndarray integration for plotkit.

This crate activates ndarray support in plotkit-core, providing IntoSeries implementations for common ndarray one-dimensional array types. Depend on this crate (or enable the ndarray feature on the plotkit umbrella crate) to pass ndarray arrays directly to any plotkit charting function.

Supported types

Type Conversion
&Array1<f64> / Array1<f64> zero-copy (contiguous slice borrow)
ArrayView1<'_, f64> zero-copy when contiguous, O(n) copy when strided
&Array1<f32> / Array1<f32> O(n) widening cast
&Array1<i32> / Array1<i32> O(n) cast to f64
&Array1<i64> / Array1<i64> O(n) cast to f64
ArrayView1<'_, f32/i32/i64> O(n) cast to f64

Examples

use ndarray::array;
use plotkit_core::series::IntoSeries;

let arr = array![1.0, 2.0, 3.0];
let series = (&arr).into_series();
assert_eq!(series.data, vec![1.0, 2.0, 3.0]);

plotkit

Publication-quality plots in 3 lines of Rust

Crates.io docs.rs CI License


Rust finally has a plotting library that looks as good as matplotlib — and it's faster.

No Python subprocess. No JavaScript runtime. No system dependencies. Just add plotkit to your Cargo.toml and start making figures that belong in a journal paper.

Quick Start

plotkit::plot(&x, &y)?;
plotkit::title("sin(x)");
plotkit::savefig("plot.png")?;

Three lines. Anti-aliased text, clean axes, and a Tableau-10 color palette out of the box.

Installation

[dependencies]
plotkit = "0.4"

Optional features:

plotkit = { version = "0.4", features = ["svg", "jupyter", "ndarray", "polars"] }
Feature Description
png PNG rasterization (enabled by default)
svg SVG vector output
jupyter Inline display in Evcxr notebooks
ndarray Plot directly from ndarray::Array1
polars Plot directly from Polars Series/DataFrame

Chart Types

plotkit supports 16 chart types with a matplotlib-familiar API:

Chart Type Method Description
Line ax.plot(x, y) Connected data points with optional markers
Scatter ax.scatter(x, y) Individual data points with colormap support
Bar ax.bar(cats, vals) Vertical bars with stacking and grouping
Horizontal Bar ax.barh(cats, vals) Horizontal bars
Grouped Bar ax.bar_group(cats, series) Side-by-side bars for multiple series
Histogram ax.hist(data, bins) Frequency distribution
Fill Between ax.fill_between(x, y1, y2) Shaded region between curves
Step ax.step(x, y) Staircase function
Stem ax.stem(x, y) Lollipop chart
Box Plot ax.boxplot(datasets) Statistical box-and-whisker
Violin ax.violin(datasets) Kernel density distribution
Error Bar ax.errorbar(x, y) Data with uncertainty ranges
Heatmap ax.heatmap(data) 2D color matrix with colorbar
Pie ax.pie(sizes) Proportional wedge chart
Contour ax.contour(x, y, z) Iso-line contour plot
Filled Contour ax.contourf(x, y, z) Filled contour regions

Two APIs

pyplot-style (quick scripts)

use plotkit::prelude::*;

let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();

plotkit::plot(&x, &y)?;
plotkit::title("sin(x)");
plotkit::xlabel("x");
plotkit::ylabel("y");
plotkit::savefig("sine.png")?;

Figure/Axes (full control)

use plotkit::prelude::*;
use plotkit::FigureExt;

let mut fig = Figure::with_size(1200, 500);

let ax1 = fig.add_subplot(1, 2, 1);
ax1.plot(&x, &sin_y)?.label("sin(x)");
ax1.plot(&x, &cos_y)?.label("cos(x)");
ax1.set_title("Trigonometric Functions");
ax1.set_xlabel("x");
ax1.set_ylabel("y");
ax1.legend();

let ax2 = fig.add_subplot(1, 2, 2);
ax2.scatter(&x, &y)?;
ax2.set_title("Scatter Plot");

fig.save("subplots.png")?;

Twin Axes

Overlay two datasets with independent y-scales:

let mut fig = Figure::with_size(800, 600);
let ax1 = fig.add_subplot(1, 1, 1);
ax1.plot(&time, &temperature)?.label("Temperature");
ax1.set_ylabel("Temperature (°C)");

let ax2 = fig.twinx(0);
ax2.plot(&time, &pressure)?.label("Pressure");
ax2.set_ylabel("Pressure (hPa)");

ax1.legend();

Colormaps & Colorbar

14 built-in colormaps: Viridis, Plasma, Inferno, Magma, Cividis, Turbo, Coolwarm, Spectral, RdYlBu, RdYlGn, PiYG, BrBG, PRGn, RdBu.

ax.heatmap(data)?.colormap(Colormap::Viridis).colorbar(true);
ax.scatter(&x, &y)?.colormap(Colormap::Plasma).c(&values);

Scales & Axis Control

ax.set_xscale(Scale::Log10);
ax.set_yscale(Scale::SymLog { linthresh: 1.0 });
ax.set_xlim(0.1, 1000.0);
ax.set_xticks(&[0.1, 1.0, 10.0, 100.0, 1000.0]);
ax.invert_yaxis();
ax.grid(true);

Annotations

ax.text(2.0, 0.8, "Peak", None);
ax.annotate(
    "Important point",
    (3.0, 0.5),   // data point
    (4.5, 0.8),   // text position
    ArrowStyle::Arrow,
);

Themes

fig.set_theme(Theme::dark());   // Dark background, light text
fig.set_theme(Theme::default()); // Clean white background

Custom themes:

let theme = Theme {
    figure_background: Color::rgb(0xF5, 0xF5, 0xF5),
    font_family: Some("Helvetica".to_string()),
    ..Theme::default()
};
fig.set_theme(theme);

DataFrame Integration

Polars

use plotkit::prelude::*;

let df = polars::df! {
    "x" => [1.0, 2.0, 3.0, 4.0],
    "y" => [1.0, 4.0, 9.0, 16.0],
}?;

ax.plot(df.column("x")?, df.column("y")?)?;

ndarray

use ndarray::Array1;

let x = Array1::linspace(0.0, 10.0, 100);
let y = x.mapv(f64::sin);
ax.plot(&x, &y)?;

Output Formats

Format Method Notes
PNG fig.save("out.png") CPU-rendered, deterministic
SVG fig.save("out.svg") Scalable vector, feature svg
Bytes fig.to_png_bytes() For embedding / streaming
String fig.to_svg_string() For web / templating

Architecture

plotkit is a multi-crate workspace:

Crate Purpose
plotkit Umbrella crate with pyplot API
plotkit-core Figure, Axes, Artists, Renderer trait
plotkit-render-skia PNG backend (tiny-skia + cosmic-text)
plotkit-render-svg SVG backend
plotkit-ndarray ndarray integration
plotkit-polars Polars integration

The Renderer trait is the abstraction boundary — core logic never depends on a specific backend. Adding a new output format means implementing one trait.

Performance

Pure-Rust CPU renderer. No GPU, no system dependencies, no surprises.

plotkit compiles to a single static binary with zero runtime dependencies. It renders plots entirely on the CPU using optimized rasterization. There is no OpenGL context to initialize, no system font lookup to fail, and no shared library to go missing on a CI server at 2 AM.

Embedded Inter font (Regular + Bold) ensures deterministic, cross-platform text rendering — the same plot looks identical on Linux, macOS, and Windows.

Roadmap

Version Focus
v0.5 WASM support — render in the browser
v0.6 3D surface plots
v0.7 Animation / frame sequences
v1.0 Stable API, full documentation

Contributing

Contributions are welcome. Whether it's a bug report, a feature request, or a pull request — we appreciate it.

Please read CONTRIBUTING.md before getting started.

License

Licensed under either of

at your option.

Dependencies

~2.5MB
~47K SLoC