Досягнення ggplot-подібних результатів matplotlib за допомогою крейту plotters мовою Rust

Наступний код Rust — це найближче, що я зміг отримати до прикладу ggplot matplotlib за допомогою крейту plotters мовою Rust.

Код 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>> {
    // Створити область малювання та налаштувати діаграму
    let root = BitMapBackend::new("sine_wave_plot.png", (1024, 768)).into_drawing_area();
    // загальний фон фігури (залишити білим)
    root.fill(&WHITE)?;

    // Визначити область діаграми (ми зробимо панель побудови світло-сірою, щоб вона була фоном сітки)
    let mut chart = ChartBuilder::on(&root)
        .caption("Синусоїда", ("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)?;

    // Заповнити область побудови світло-сірим, щоб вона була фоном сітки
    // matplotlib ggplot: axes.facecolor = E5E5E5
    chart.plotting_area().fill(&RGBColor(0xE5, 0xE5, 0xE5))?;

    // Налаштувати сітку (стиль ggplot з ледь помітними лініями сітки)
    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("Кут (радіани)")
        .y_desc("Амплітуда")
        // 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 (основна сітка); другорядна сітка вимкнена за замовчуванням
        .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))
        // забезпечити зовнішні позначки (від'ємні вказували б всередину)
        .set_all_tick_mark_size(5)
        .draw()?;

    // Згенерувати точки даних синусоїди
    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()));
    }

    // Намалювати синусоїду
    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)));

    // Намалювати легенду
    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"

Графік синусоїди (Python).avif

Еталонний код Python

sine_wave_plot.py
#!/usr/bin/env python3
"""Згенерувати графік синусоїди у стилі ggplot та зберегти у `sine_wave_plot_py.png`.

Використання:
    python3 plot_sine.py

Встановлення залежностей:
    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")

# Дані
x = np.linspace(0, 2 * np.pi, 1000)
y = np.sin(x)

# Розмір фігури наближено до 1024x768 при 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)")

# Форматування позначок осі X кратними π
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))

# Підписи, заголовок, легенда
ax.set_xlabel("Кут (радіани)", fontsize=16)
ax.set_ylabel("Амплітуда", fontsize=16)
ax.set_title("Синусоїда", fontsize=24, fontweight="bold")
ax.legend(fontsize=12)

# Сітка та компонування
ax.grid(True)
plt.tight_layout()

# Зберегти файл
plt.savefig("sine_wave_plot_py.png", dpi=100)
# Розкоментуйте для інтерактивного показу
# plt.show()

Графік синусоїди (Rust).avif

Обговорення відмінностей

Примітка: Я не намагався жодним чином підібрати розмір шрифту тощо.

Як видно, з певними ручними зусиллями два графіки виглядають досить схожими. Однак, оскільки згладжування не реалізовано в crate plotters поки що (станом на 2026-01-30), графік Rust виглядає значно більш «зубчастим», ніж графік matplotlib.

Коли йдеться про графіки публікаційної якості, це суттєвий недолік, і з цієї причини я не можу рекомендувати. Сподіваюся, згладжування буде реалізовано в майбутньому.

Крупний план згладжування Python

Згладжування Python.avif

Крупний план «згладжування» Rust (відсутнє)

Згладжування Rust.avif


Дивіться схожі статті за категоріями: Rust Python Data Visualization