Lograr resultados similares a ggplot de matplotlib usando el crate plotters de Rust

El siguiente código en Rust es lo más cerca que pude llegar al ejemplo de ggplot de matplotlib usando el crate plotters de Rust.

Código en Rust

src/main.rs
use plotters::prelude::*;
use plotters::style::{RGBColor, ShapeStyle};
use std::error::Error;

const GGPLOT_RED: RGBColor = RGBColor(0xE2, 0x4A, 0x33);

fn main() -> Result<(), Box<dyn Error>> {
    // Crear un área de dibujo y configurar el gráfico
    let root = BitMapBackend::new("sine_wave_plot.png", (1024, 768)).into_drawing_area();
    // fondo general de la figura (mantenerlo blanco)
    root.fill(&WHITE)?;

    // Definir el área del gráfico (haremos el panel de graficación gris claro para que sea el fondo de la cuadrícula)
    let mut chart = ChartBuilder::on(&root)
        .caption("Sine Wave", ("sans-serif", 30).into_font())
        .margin(24)
        .x_label_area_size(50)
        .y_label_area_size(50)
        .build_cartesian_2d(0f64..2f64 * std::f64::consts::PI, -1.2f64..1.2f64)?;

    // Rellenar el área de graficación con un gris claro para que sea el fondo de la cuadrícula
    // matplotlib ggplot: axes.facecolor = E5E5E5
    chart.plotting_area().fill(&RGBColor(0xE5, 0xE5, 0xE5))?;

    // Configurar la malla (estilo similar a ggplot con líneas de cuadrícula sutiles)
    chart.configure_mesh()
        .x_labels(10)
        .y_labels(5)
        .x_label_formatter(&|x| format!("{:.1}π", x / std::f64::consts::PI))
        .y_label_formatter(&|y| format!("{:.1}", y))
        .x_desc("Angle (radians)")
        .y_desc("Amplitude")
        // matplotlib ggplot: axes.labelcolor/xtick.color/ytick.color = 555555
        .label_style(TextStyle::from(("sans-serif", 12).into_font()).color(&RGBColor(0x55, 0x55, 0x55)))
        .axis_desc_style(TextStyle::from(("sans-serif", 16).into_font()).color(&RGBColor(0x55, 0x55, 0x55)))
        // matplotlib ggplot: grid.color = white (cuadrícula mayor); la cuadrícula menor está desactivada por defecto
        .max_light_lines(0)
        .bold_line_style(ShapeStyle::from(&WHITE).stroke_width(1))
        .light_line_style(ShapeStyle::from(&WHITE).stroke_width(1))
        // matplotlib ggplot: axes.edgecolor = white, axes.linewidth = 1
        .axis_style(ShapeStyle::from(&WHITE).stroke_width(1))
        // asegurar marcas hacia afuera (negativo apuntaría hacia adentro)
        .set_all_tick_mark_size(5)
        .draw()?;

    // Generar puntos de datos de la onda senoidal
    let mut sine_data = Vec::new();
    for i in 0..=1000 {
        let x = i as f64 * 2.0 * std::f64::consts::PI / 1000.0;
        sine_data.push((x, x.sin()));
    }

    // Dibujar la onda senoidal
    chart.draw_series(LineSeries::new(
        sine_data,
        ShapeStyle::from(&GGPLOT_RED).stroke_width(2),
    ))?
    .label("sin(x)")
    .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], ShapeStyle::from(&GGPLOT_RED).stroke_width(3)));

    // Dibujar la leyenda
    chart.configure_series_labels()
        .background_style(&RGBColor(0xE5, 0xE5, 0xE5).mix(0.9))
        .border_style(&WHITE)
        .label_font(("sans-serif", 14))
        .position(SeriesLabelPosition::UpperRight)
        .draw()?;

    Ok(())
}
Cargo.toml
[package]
name = "plottest"
version = "0.1.0"
edition = "2021"

[dependencies]
plotters = "0.3.5"

sine wave plot py.avif

Código Python de referencia

sine_wave_plot.py
#!/usr/bin/env python3
"""Genera un gráfico de onda senoidal en estilo ggplot y lo guarda en `sine_wave_plot_py.png`.

Uso:
    python3 plot_sine.py

Instalar dependencias:
    python3 -m pip install -r requirements.txt
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

plt.style.use("ggplot")

# Datos
x = np.linspace(0, 2 * np.pi, 1000)
y = np.sin(x)

# Tamaño de figura para aproximar 1024x768 a 100 DPI
fig, ax = plt.subplots(figsize=(10.24, 7.68))
ax.plot(x, y, color="red", alpha=0.8, linewidth=2, label="sin(x)")

# Formatear marcas de x en múltiplos de π
def pi_label(value, _pos):
    val = value / np.pi
    if abs(val) < 1e-6:
        return "0"
    return f"{val:.1f}π"

ax.xaxis.set_major_locator(plt.MaxNLocator(10))
ax.xaxis.set_major_formatter(FuncFormatter(pi_label))

# Etiquetas, título, leyenda
ax.set_xlabel("Angle (radians)", fontsize=16)
ax.set_ylabel("Amplitude", fontsize=16)
ax.set_title("Sine Wave", fontsize=24, fontweight="bold")
ax.legend(fontsize=12)

# Cuadrícula y diseño
ax.grid(True)
plt.tight_layout()

# Guardar archivo
plt.savefig("sine_wave_plot_py.png", dpi=100)
# Descomentar para mostrar de forma interactiva
# plt.show()

sine wave plot rust.avif

Discusión de las diferencias

Nota: No intenté igualar el tamaño de fuente, etc., de ninguna manera.

Como puedes ver, con algo de esfuerzo manual, los dos gráficos se ven bastante similares. Sin embargo, como el antialiasing no está implementado en el crate plotters todavía (a fecha de 2026-01-30), el gráfico de Rust se ve significativamente más “dentado” que el de matplotlib.

Cuando se habla de gráficos de calidad para publicación, esto es una desventaja significativa y por esta razón no puedo recomendarlo. Con suerte, el antialiasing se implementará en el futuro.

Acercamiento al antialiasing de Python

Python Anti Aliasing.avif

Acercamiento al “antialiasing” de Rust (ninguno)

Rust Anti Aliasing.avif


Echa un vistazo a artículos similares por categoría: Rust, Python, Data Visualization