Skip to content
datavilPublic

About

Rust implementation of ggplot (lets-plot)

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

315 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ggrust logo

ggrust

GitHub License: MIT Docs

The Grammar of Graphics, written in Rust, used from Python.

Median render time per plot at 1M rows: ggrust 0.06 s, ggplot2 0.33 s, Lets-Plot 5.86 s

Median time to render one of 11 everyday plots with 1M rows, as SVG and PNG (benchmarks).

ggrust is a plotting library with a Lets-Plot compatible Python API. Plots are built the familiar ggplot way, layer by layer with +, while the stats, scales, layout and rendering all run in Rust.

It was started for Cellestial, which needed a maintained backend for its Lets-Plot based API.

Clustered heatmap with a row dendrogram and a side annotation track Penguin species outlined by labelled hulls Scatter plot with density side panels Boxplots with significance brackets

Every plot above is ggrust. More in the gallery.

Highlights

  • Fast and lean: at 1M rows a typical plot renders 5x faster than ggplot2 and 90x faster than Lets-Plot, with 4x and 7.5x less memory (benchmarks).
  • ggplot2 extensions, built in: beeswarms, significance brackets, cluster marks, side panels, dendrograms, raincloud plots, patchwork layouts and more, under their R names (list).
  • The Lets-Plot API: most code ports with an import swap, lets_plot -> ggrust.
  • Interactive: notebook and HTML output with tooltips, zoom and pan; dense point layers drawn on a canvas.
  • Publication output: SVG, PNG, vector PDF and HTML, with your own fonts. Data crosses into Rust once, as Arrow, so Polars and pandas frames work directly.

Installation

pip install ggrust
uv add ggrust

Quickstart

import polars as pl
from ggrust import *

frame = pl.DataFrame(
    {
        "height": [150, 162, 171, 180, 168, 175],
        "weight": [52, 60, 68, 82, 65, 74],
        "group": ["a", "a", "b", "b", "c", "c"],
    }
)

plot = (
    ggplot(frame, aes(x="height", y="weight", color="group"))
    + geom_point(size=4)
    + labs(title="Height vs weight")
    + ggsize(600, 400)
)

plot.to_png("plot.png")

Scatter plot coloured by group

In a notebook the plot shows as interactive HTML (tooltips, zoom and pan with ggtb()).

ggplot2 extensions, built in

Many popular ggplot2 extension packages from R are built into ggrust, under their R function names. Nothing extra to install; Lets-Plot has none of them.

Feature ggrust From the R package
Beeswarm and quasirandom points geom_beeswarm, geom_quasirandom ggbeeswarm
Journal color palettes scale_color_npg, _aaas, _nejm, _lancet, _jama, _jco, _d3, _igv ggsci
Significance tests and labels stat_compare_means, stat_cor, stat_poly_eq ggpubr, ggpmisc
Cluster marks geom_mark_hull, geom_mark_ellipse, geom_mark_rect ggforce
Highlighting gghighlight gghighlight
Point density geom_pointdensity ggpointdensity
Side panels geom_xside*, geom_yside*, ggmarginal ggside
Several color scales in one plot new_scale_fill, new_scale_color, new_scale ggnewscale
Dendrograms hclust, scale_x_dendro, scale_y_dendro, geom_dendrogram legendry, ggh4x, ggdendro
Raincloud plots stat_halfeye, stat_interval, geom_half_violin, geom_half_boxplot, geom_half_point ggdist, gghalves
Figure layouts and panel tags gggrid(design=, tag_levels=), inset_element patchwork
Rasterized layers in vector output rasterize ggrastr, scattermore

ggrust reimplements their behavior in Rust; the R packages and their authors designed it. Examples and credits are in the extensions guide.

Performance

11 everyday plots (scatter, histogram, boxplot, violin, density, lm smooth, line, 2D bins, facets and more) at 10k, 100k and 1M rows, each saved as SVG and PNG. Same data, every row drawn (Lets-Plot's sampling turned off), same output size, each run in a fresh process. Apple M4 Pro; ggrust main (0.8.0 plus a line-drawing fix), R ggplot2 4.0.1 (svglite, ragg), lets-plot 4.11.0.

1M rows SVG, all 11 plots PNG, all 11 plots peak memory per plot
ggrust 0.91 s 2.85 s 138 MB
ggplot2 9.67 s (10.6x) 25.0 s (8.8x) 560 MB
Lets-Plot 71.6 s (78.9x) > 495 s (> 174x) 1.0 GB
  • ggrust is faster than both in all 66 cases (11 plots x 3 sizes x 2 formats).
  • Lets-Plot did not finish the 1M-row line PNG within 300 s; it counts as 300 s in its total.

Charts, rules and how to reproduce: the benchmarks page. Raw numbers: benchmarks/REPORT-three-way.md.

More examples

Distributions

Violins with boxplots on top, using the same layering as ggplot.

Show code
import numpy as np

rng = np.random.default_rng(0)
days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
commute = pl.DataFrame(
    {
        "day": np.repeat(days, 200),
        "minutes": np.concatenate([rng.gamma(4 + i, 6, 200) for i in range(5)]),
    }
)

(
    ggplot(commute, aes(x="day", y="minutes", fill="day"))
    + geom_violin(alpha=0.6)
    + geom_boxplot(width=0.15, fill="white")
    + labs(title="Commute time by weekday")
    + theme_classic()
    + ggsize(600, 400)
)

Violin and boxplot of commute times

Facets

Split a plot into panels by a column.

Show code
months = np.arange(1, 13)
temperature = pl.DataFrame(
    {
        "month": np.tile(months, 3),
        "temp": np.concatenate(
            [10 + 8 * np.sin((months - 3) / 12 * 2 * np.pi) + offset for offset in (0, 5, -4)]
        ),
        "city": np.repeat(["Lisbon", "Athens", "Oslo"], 12),
    }
)

(
    ggplot(temperature, aes(x="month", y="temp", color="city"))
    + geom_line(size=1.2)
    + geom_point()
    + facet_wrap("city", ncol=3)
    + labs(title="Monthly temperature", y="°C")
    + theme_bw()
    + ggsize(900, 350)
)

Monthly temperature faceted by city

What it covers

  • Geoms: point, line, path, bar, col, histogram, boxplot, violin, area ridges, tile, raster, rect, text, label, text/label repel, segment, curve, polygon, rug, imshow, brackets and more.
  • Stats: count, bin, density, smooth, summary, ecdf, contours, 2D bins, ellipses.
  • Scales: continuous, discrete, log, reversed, datetime, hue, manual, gradients, viridis, brewer, secondary axes, axes on any side.
  • Layout: facet_wrap, facet_grid, gggrid with collected legends, coord_fixed, coord_flip, themes (theme_classic, theme_bw, theme_minimal, theme_void, ...).
  • Outputs: SVG, PNG, vector PDF, and interactive HTML through ggsave, with your own fonts (register_font). Dense scatter layers are drawn on a canvas in HTML.

ggrust is a subset of Lets-Plot, not all of it. Features follow what Cellestial and everyday plotting need. See the migration note for the differences.

Status

Early, but usable: the full Cellestial test suite passes on ggrust. Expect rough edges and API gaps; issues are welcome.

Development

uv sync          # create the dev environment
make develop     # build the Rust extension into .venv
make check       # fmt, clippy, ruff, cargo test, pytest

Roadmap in plan.md, contributor rules in AGENTS.md.

About

Rust implementation of ggplot (lets-plot)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages