Erreichen von matplotlib ggplot-ähnlichen Ergebnissen mit dem Rust plotters-Crate

Der folgende Rust-Code ist das Nähest, was ich dem matplotlib-ggplot-Beispiel mit dem Rust-plotters-Crate erreichen konnte.

Rust-Code

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>> {
    // Erstelle einen Zeichenbereich und richte das Diagramm ein
    let root = BitMapBackend::new("sine_wave_plot.png", (1024, 768)).into_drawing_area();
    // allgemeiner Abbildungshintergrund (weiß lassen)
    root.fill(&WHITE)?;

    // Definiere den Diagrammbereich (wir machen das Plot-Panel hellgrau, um es als Gitterhintergrund zu nutzen)
    let mut chart = ChartBuilder::on(&root)
        .caption("Sinuswelle", ("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)?;

    // Fülle den Plotbereich mit Hellgrau als Gitterhintergrund
    // matplotlib ggplot: axes.facecolor = E5E5E5
    chart.plotting_area().fill(&RGBColor(0xE5, 0xE5, 0xE5))?;

    // Konfiguriere das Gitter (ggplot-ähnlicher Stil mit dezenten Gitterlinien)
    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("Winkel (Bogenmaß)")
        .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 (Hauptgitter); Nebengitter ist standardmäßig deaktiviert
        .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))
        // stelle äußere Tickmarkierungen sicher (negative würde nach innen zeigen)
        .set_all_tick_mark_size(5)
        .draw()?;

    // Generiere Sinuswellen-Datenpunkte
    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()));
    }

    // Zeichne die Sinuswelle
    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)));

    // Zeichne die Legende
    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

Referenz-Python-Code

sine_wave_plot.py
#!/usr/bin/env python3
"""Generiere einen Sinuswellen-Plot im ggplot-Stil und speichere in `sine_wave_plot_py.png`.

Verwendung:
    python3 plot_sine.py

Abhängigkeiten installieren:
    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")

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

# Abbildungsgröße approximiert 1024x768 bei 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)")

# Formatiere x-Ticks in Vielfachen von π
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))

# Beschriftungen, Titel, Legende
ax.set_xlabel("Winkel (Bogenmaß)", fontsize=16)
ax.set_ylabel("Amplitude", fontsize=16)
ax.set_title("Sinuswelle", fontsize=24, fontweight="bold")
ax.legend(fontsize=12)

# Gitter und Layout
ax.grid(True)
plt.tight_layout()

# Datei speichern
plt.savefig("sine_wave_plot_py.png", dpi=100)
# Einkommentieren, um interaktiv anzuzeigen
# plt.show()

sine wave plot rust.avif

Diskussion der Unterschiede

Hinweis: Ich habe nicht versucht, die Schriftgröße etc. in irgendeiner Weise anzupassen.

Wie du sehen kannst, sehen die beiden Plots mit etwas manuellem Aufwand recht ähnlich aus. Jedoch, da Antialiasing noch nicht implementiert ist im plotters-Crate (Stand 2026-01-30), sieht der Rust-Plot deutlich “gezackter” aus als der matplotlib-Plot.

Wenn man über Publikationsqualitäts-Plots spricht, ist dies ein erheblicher Nachteil und aus diesem Grund kann ich es nicht empfehlen. Hoffentlich wird Antialiasing in Zukunft implementiert.

Nahaufnahme des Python-Antialiasing

Python Anti Aliasing.avif

Nahaufnahme des Rust-“Antialiasing” (keines)

Rust Anti Aliasing.avif


Ähnliche Beiträge nach Kategorie: Rust, Python, Data Visualization