【rust】《Rust深度学习[3]-数据可视化库(Plotters)》

什么是Plotters?

Plotters是一个用纯Rust开发的图形库,用于中渲染图形、图表和数据可视化。它支持静态图片渲染和实时渲染,并支持多种后端,包括:位图格式(png、bmp、gif等)、矢量图(svg)、窗口和HTML5 Canvas。

Plotters对不同后端使用统一的高级API,并允许开发者自定义坐标系。在Plotters中,任何类型的图表都被抽象为一系列绘图操作,通过这些绘图操作,开发者可以自由地操控和组合绘图内容。因此Plotters不会限制图表类型,开发者可以组合出任意内容的图表。

导入依赖库

[dependencies]
plotters = "0.3.3"

折线图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("折线图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        .build_cartesian_2d(0.0..10.0, 0.0..10.0)?;
    // 配置网格线
    chart.configure_mesh().draw()?;
    // 绘制折线
    chart.draw_series(LineSeries::new(
        // vec![(0., 2.), (1., 1.), (2., 2.), (3., 3.), (4., 4.), (5., 5.), (6., 6.), (7., 7.), (8., 8.), (9., 9.)],
        vec![0., 1., 2., 3., 4., 5., 6., 7., 8.].iter().map(|x| (*x, *x)),
        // 线条颜色
        &BLUE,
    ))?;

    Ok(())
}

多折线图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("多折线图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        .build_cartesian_2d(0.0..10.0, 0.0..10.0)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 绘制多折线图
    // 折线1
    chart.draw_series(LineSeries::new(
        // x.powf(2) 求x的2次幂
        (0..10).map(|x| (x as f64, (x as f64).powf(2.0))),
        &RED.stroke_width(5).color,
        // 标签
    ))?.label("y=x^2")
        .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));

    // 折线2
    chart.draw_series(LineSeries::new(
        (0..10).map(|x| (x as f64, (x as f64).powf(2.0) + 2.0)),
        &BLUE.stroke_width(5).color,
        // 标签
    ))?.label("y=x^2+2")
        .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE));

    // 配置标签样式
    chart.configure_series_labels()
        .background_style(&WHITE.mix(0.8))
        .border_style(&BLACK)
        .draw()?;
    Ok(())
}

散点图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("散点图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        .build_cartesian_2d(0.0..10.0, 0.0..10.0)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 散点位置
    let data = [
        (1., 2.),
        (2., 3.),
        (3., 4.),
        (4., 5.),
        (5., 6.),
    ];
    // 绘制散点
    chart.draw_series(
        data.iter()
            .map(|(x, y)| {
                Circle::new((*x, *y), 5, BLUE.filled())
            })
            .collect::<Vec<_>>(),
    )?;

    Ok(())
}

多标记散点图

use plotters::prelude::*;

const DATA1: [(i32, i32); 30] = [
    (-3, 1), (-2, 3), (4, 2), (3, 0), (6, -5), (3, 11), (6, 0), (2, 14), (3, 9), (14, 7), (8, 11),
    (10, 16), (7, 15), (13, 8), (17, 14), (13, 17), (19, 11), (18, 8), (15, 8), (23, 23), (15, 20),
    (22, 23), (22, 21), (21, 30), (19, 28), (22, 23), (30, 23), (26, 35), (33, 19), (26, 19)
];
const DATA2: [(i32, i32); 30] = [
    (1, 22), (0, 22), (1, 20), (2, 24), (4, 26), (6, 24), (5, 27), (6, 27), (7, 27), (8, 30), (10, 30),
    (10, 33), (12, 34), (13, 31), (15, 35), (14, 33), (17, 36), (16, 35), (17, 39), (19, 38), (21, 38),
    (22, 39), (23, 43), (24, 44), (24, 46), (26, 47), (27, 48), (26, 49), (28, 47), (28, 50)
];

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("散点图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        .build_cartesian_2d(-10..50, -10..50)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 设置三角形标记散点
    chart.draw_series(
        DATA1.iter().map(
            |point| TriangleMarker::new(*point, 5, &BLUE)
        ),
    )?.label("三角")
        .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE));
    // 设置圆圈标记散点
    chart.draw_series(
        DATA2.iter().map(
            |point| Circle::new(*point, 5, &RED)
        ),
    )?.label("圆圈")
        .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));

    // 配置标签样式
    chart.configure_series_labels()
        .background_style(&WHITE.mix(0.8))
        .border_style(&BLACK)
        .draw()?;

    Ok(())
}

面积图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("面积图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        .build_cartesian_2d(0..10, 0..50)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    let data = [25, 37, 15, 32, 45, 33, 32, 10, 29, 0, 21];

    let temp = (0..).zip(data.iter().map(|x| *x));
    println!("{:?}", temp);

    chart.draw_series(
        // 绘制面积图
        AreaSeries::new(
            // { a: 0.., b: Map { iter: Iter([25, 37, 15, 32, 45, 33, 32, 10, 29, 0, 21]) } }
            // 0.. 顺序x轴
            (0..).zip(data.iter().map(|x| *x)),
            // 基线
            0,
            // 透明度
            &RED.mix(0.2) 
        ).border_style(&RED) // 最亮边线的样式
    )?;

    Ok(())
}

柱状图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("柱状图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        // 重点!!!! 这里的传值必须为SegmentedCoord类型
        .build_cartesian_2d((0..10).into_segmented(), 0..50)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 数值
    let data = [25, 37, 15, 32, 45, 33, 32, 10, 0, 21, 5];

    chart.draw_series(
        (0..).zip(data.iter()).map(
            |(x, y)| {
                // 创建x轴底部分段对象
                let x0 = SegmentValue::Exact(x);
                // 创建x轴顶部分段对象
                let x1 = SegmentValue::Exact(x + 1);
                let mut bar = Rectangle::new([(x0, 0), (x1, *y)], RED.filled());
                // 柱左右间距
                bar.set_margin(0, 0, 5, 5);
                bar
            }
        ))?;
    Ok(())
}

水平柱状图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("水平柱状图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        // 重点!!!! 这里的传值必须为SegmentedCoord类型
        .build_cartesian_2d(0..50, (0..10).into_segmented())?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 数值
    let data = [25, 37, 15, 32, 45, 33, 32, 10, 0, 21, 5];
    // y轴
    let index = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    chart.draw_series(
        // 循环x轴数据
        // 压缩y轴生成结构
        data.iter().zip(index.iter()).map(
            |(x, y)| {
                // 创建y轴左部分段对象
                let y0 = SegmentValue::Exact(*y);
                // 创建y轴右部分段对象
                let y1 = SegmentValue::Exact(*y + 1);
                let mut bar = Rectangle::new([(0, y0), (*x, y1)], GREEN.filled());
                // 设置上下柱间距
                bar.set_margin(5, 5, 0, 0);
                bar
            }
        ))?;
    Ok(())
}

真假柱状图

use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建svg图片对象
    let root = SVGBackend::new("plot.svg", (640, 480)).into_drawing_area();
    // 图片对象的背景颜色填充
    root.fill(&WHITE)?;
    // 创建绘图对象
    let mut chart = ChartBuilder::on(&root)
        // 图表名称  (字体样式, 字体大小)
        .caption("真假柱状图", ("sans-serif", 30))
        // 图表左侧与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Left, 40)
        // 图表底部与图片边缘的间距
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        // 构建二维图像, x轴 0.0 - 10.0; y轴 0.0 - 10.0;
        // 重点!!!! 这里的传值必须为SegmentedCoord类型
        .build_cartesian_2d([true, false].into_segmented(), 0..50)?;
    // 配置网格线
    chart.configure_mesh().draw()?;

    // 数值
    let prim:Vec<_> = (2..50).map(is_prime).collect();

    chart.draw_series(
        Histogram::vertical(&chart)
            .margin(100)
            .data(prim.iter().map(|x| (x, 1)))
    )?;
    Ok(())
}

fn is_prime(n: i32) -> bool {
    for i in 2..n {
        if n % i == 0 {
            return false;
        }
    }
    true
}

箱式图

use chrono::{Duration, NaiveDate};
use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 获取股票数据
    let data = get_data();
    // 创建绘图区域,大小为 1024x768
    let root = BitMapBackend::new("stock.png", (1024, 768)).into_drawing_area();
    // 填充白色背景
    root.fill(&WHITE)?;

    // 计算数据的起始和结束日期
    let (to_date, from_date) = (
        parse_time(data[0].0) + Duration::days(1),
        parse_time(data[29].0) - Duration::days(1),
    );

    // 创建图表构建器
    let mut chart = ChartBuilder::on(&root)
        .x_label_area_size(40)
        .y_label_area_size(40)
        .caption("MSFT Stock Price", ("sans-serif", 50.0).into_font())
        .build_cartesian_2d(from_date..to_date, 110f32..135f32)?;

    // 配置网格样式
    chart.configure_mesh().light_line_style(WHITE).draw()?;

    // 绘制蜡烛图
    chart.draw_series(
        data.iter().map(|x| {
            CandleStick::new(parse_time(x.0), x.1, x.2, x.3, x.4, GREEN.filled(), RED, 15)
        }),
    )?;

    // 保存图表到文件
    root.present().expect("无法将结果写入文件,请确保当前目录下存在该目录");

    Ok(())
}

// 解析日期字符串为 NaiveDate 类型
fn parse_time(t: &str) -> NaiveDate {
    NaiveDate::parse_from_str(&format!("{} 0:0", t), "%Y-%m-%d %H:%M").unwrap()
}

// 获取股票数据
fn get_data() -> Vec<(&'static str, f32, f32, f32, f32)> {
    vec![
        ("2019-04-25", 130.06, 131.37, 128.83, 129.15),
        ("2019-04-24", 125.79, 125.85, 124.52, 125.01),
        ("2019-04-23", 124.1, 125.58, 123.83, 125.44),
        ("2019-04-22", 122.62, 124.0000, 122.57, 123.76),
        ("2019-04-18", 122.19, 123.52, 121.3018, 123.37),
        ("2019-04-17", 121.24, 121.85, 120.54, 121.77),
        ("2019-04-16", 121.64, 121.65, 120.1, 120.77),
        ("2019-04-15", 120.94, 121.58, 120.57, 121.05),
        ("2019-04-12", 120.64, 120.98, 120.37, 120.95),
        ("2019-04-11", 120.54, 120.85, 119.92, 120.33),
        ("2019-04-10", 119.76, 120.35, 119.54, 120.19),
        ("2019-04-09", 118.63, 119.54, 118.58, 119.28),
        ("2019-04-08", 119.81, 120.02, 118.64, 119.93),
        ("2019-04-05", 119.39, 120.23, 119.37, 119.89),
        ("2019-04-04", 120.1, 120.23, 118.38, 119.36),
        ("2019-04-03", 119.86, 120.43, 119.15, 119.97),
        ("2019-04-02", 119.06, 119.48, 118.52, 119.19),
        ("2019-04-01", 118.95, 119.1085, 118.1, 119.02),
        ("2019-03-29", 118.07, 118.32, 116.96, 117.94),
        ("2019-03-28", 117.44, 117.58, 116.13, 116.93),
        ("2019-03-27", 117.875, 118.21, 115.5215, 116.77),
        ("2019-03-26", 118.62, 118.705, 116.85, 117.91),
        ("2019-03-25", 116.56, 118.01, 116.3224, 117.66),
        ("2019-03-22", 119.5, 119.59, 117.04, 117.05),
        ("2019-03-21", 117.135, 120.82, 117.09, 120.22),
        ("2019-03-20", 117.39, 118.75, 116.71, 117.52),
        ("2019-03-19", 118.09, 118.44, 116.99, 117.65),
        ("2019-03-18", 116.17, 117.61, 116.05, 117.57),
        ("2019-03-15", 115.34, 117.25, 114.59, 115.91),
        ("2019-03-14", 114.54, 115.2, 114.33, 114.59),
    ]
}

 

其他可视化图-参考地址:https://plotters-rs.github.io/book/basic/draw_3d_plots.html

https://github.com/plotters-rs/plotters/tree/master?tab=readme-ov-file

 

部分内容来源:https://jarod.blog.csdn.net/article/details/128126174

posted @ 2024-04-23 13:23  芋白  阅读(2157)  评论(0)    收藏  举报