Plot 如何绘制x轴上有日期,y轴上有时间的序列?

Plot 如何绘制x轴上有日期,y轴上有时间的序列?,plot,rust,Plot,Rust,我想绘制点,其中x坐标是日期,y坐标是时间(就像Chrono的NaiveTime) 我认为这将是最简单的使用板条箱。 不幸的是,默认情况下,它不支持y轴上的时间,如下所示。然而,该文件指出 理论上,绘图仪支持任何数据类型。唯一的要求是实现轴映射特性 听起来不错 因此,我采用了他们的例子,并得出以下结论 use chrono::{Date, Duration, ParseError, DateTime, Utc, NaiveTime}; use chrono::offset::{Local, Ti

我想绘制点,其中x坐标是日期,y坐标是时间(就像Chrono的
NaiveTime

我认为这将是最简单的使用板条箱。 不幸的是,默认情况下,它不支持y轴上的时间,如下所示。然而,该文件指出

理论上,绘图仪支持任何数据类型。唯一的要求是实现轴映射特性

听起来不错

因此,我采用了他们的例子,并得出以下结论

use chrono::{Date, Duration, ParseError, DateTime, Utc, NaiveTime};
use chrono::offset::{Local, TimeZone};
use plotters::prelude::*;

fn parse_datetime(t: &str) -> Date<Local> {
    Local
        .datetime_from_str(&format!("{} 0:0", t), "%Y-%m-%d %H:%M")
        .unwrap()
        .date()
}

/// Workaround attempt: use a mock Date in order to get a DateTime instead of a NaiveTime
fn parse_time_as_datetime(t: &str) -> Result<DateTime<Local>, ParseError> {
    return match Local.datetime_from_str(&format!("2020-01-01 {}", t), "%Y-%m-%d %H:%M.%S") {
        Ok(date) => Ok(date),
        Err(e) => { println!("{}", e); Err(e) },
    };
}

fn parse_time(t: &str) -> Result<NaiveTime, ParseError> {
    return match Local.datetime_from_str(t, "%M:%S%.f") {
        Ok(date) => Ok(date.time()),
        Err(e) => { println!("{}", e); Err(e) },
    };
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = get_data();
    let root = BitMapBackend::new("stock-example.png", (1024, 768)).into_drawing_area();
    root.fill(&WHITE)?;

    let (from_date, to_date) = (
        parse_datetime(&data[0].0) + Duration::days(1),
        parse_datetime(&data[4].0) - Duration::days(1),
    );

    let y_min = parse_time("9:30.0").unwrap();
    let y_max = parse_time("13:00.0").unwrap();
    // Workaround attempt: use DateTime instead of NaiveTime
    // let y_min = Local.datetime_from_str("2020-01-01 0:0", "%Y-%m-%d %H:%M").unwrap().date();
    // let y_max = Local.datetime_from_str("2020-01-02 0:1", "%Y-%m-%d %H:%M").unwrap().date();

    let mut chart = ChartBuilder::on(&root)
        .x_label_area_size(40)
        .y_label_area_size(40)
        .caption("Time", ("sans-serif", 30.0).into_font())
        .build_cartesian_2d(from_date..to_date, y_min..y_max)?;

    chart.configure_mesh().light_line_style(&WHITE).draw()?;

    chart.draw_series(
        data.iter()
            .map(|x| Circle::new((parse_datetime(x.0), parse_time(x.1).unwrap()), 5, BLUE.filled())),
    )?;

    Ok(())
}

fn get_data() -> Vec<(&'static str, &'static str, f32, f32, f32)> {
    return vec![
        ("2019-04-18", "10:11.5", 16.0, 121.3018, 123.3700),
        ("2019-04-22", "10:52.2", 15.0, 122.5700, 123.7600),
        ("2019-04-23", "12:23.5", 14.0, 123.8300, 125.4400),
        ("2019-04-24", "10:15.0", 13.0, 124.5200, 125.0100),
        ("2019-04-25", "10:43.9", 12.0, 128.8300, 129.1500),
    ];
}
因此,我们更改为将结构字段公开:
pub-struct-RangedDateTime(pub-DT,pub-DT)。
它失败了

52  |         .build_cartesian_2d(from_date..to_date, RangedDateTime(y_min, y_max))?;
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `RangedDateTime { 0: val, 1: val }`
52 |         .build_cartesian_2d(from_date..to_date, RangedDateTime(y_min, y_max))?;
   |                                                 ^^^^^^^^^^^^^^ the trait `ranged1d::types::datetime::TimeValue` is not implemented for `NaiveTime`
但是,我们无法为
NaiveTime
实现
TimeValue
,因为这是当前的定义:

pub trait TimeValue: Eq {
    type DateType: Datelike + PartialOrd;
    // ...
}
而且
NaiveTime
不是
DateLike
。 我不知道如何从这里开始,我考虑了以下选项:

  • TimeLike
    添加到
    TimeValue
    关联类型的限制中
  • 从关联的
    TimeValue
    类型中删除
    DateLike
  • 添加一个单独的trait
    DateTimeValue
    ,它可以将
    TimeLike
    作为其
    DateType

在这种情况下,从Chrono更容易使用,因为绘图仪也实现了
RangedDuration
,只是有点隐藏

注意:您可以使用自定义轴标签格式化程序,因此如果需要,您仍然可以将其格式化为一天中的时间

固定示例:

#![feature(allocator_api)]

use chrono::{Date, Duration, ParseError, NaiveTime};
use chrono::offset::{Local, TimeZone};
use plotters::prelude::*;

fn parse_datetime(t: &str) -> Date<Local> {
    Local
        .datetime_from_str(&format!("{} 0:0", t), "%Y-%m-%d %H:%M")
        .unwrap()
        .date()
}

fn parse_time(t: &str) -> Result<Duration, ParseError> {
    return match Local.datetime_from_str(&format!("2020-01-01 0:{}", t), "%Y-%m-%d %H:%M:%S%.f") {
        Ok(date) => Ok(date.time().signed_duration_since(NaiveTime::from_hms(0, 0, 0))),
        Err(e) => { println!("{}", e); Err(e) },
    };
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = get_data();
    let root = BitMapBackend::new("stock-example.png", (1024, 768)).into_drawing_area();
    root.fill(&WHITE)?;

    let (from_date, to_date) = (
        parse_datetime(&data[0].0) - Duration::days(1),
        parse_datetime(&data[data.len() - 1].0) + Duration::days(1),
    );

    let y_min = parse_time("9:30.0").unwrap();
    let y_max = parse_time("13:00.0").unwrap();

    let mut chart = ChartBuilder::on(&root)
        .x_label_area_size(40)
        .y_label_area_size(50)
        .caption("Time", ("sans-serif", 30.0).into_font())
        .build_cartesian_2d(from_date..to_date, y_min..y_max)?;

    chart.configure_mesh()
        .light_line_style(&WHITE)
        .y_label_formatter(&|y| format!("{:02}:{:02}", y.num_minutes(), y.num_seconds() % 60))
        .x_label_formatter(&|x| x.naive_local().to_string())
        .draw()?;

    chart.draw_series(
        data.iter()
            .map(|x| Circle::new((parse_datetime(x.0), parse_time(x.1).unwrap()), 5, BLUE.filled())),
    )?;

    Ok(())
}

fn get_data() -> Vec<(&'static str, &'static str, f32, f32, f32)> {
    return vec![
        ("2019-04-18", "10:11.5", 16.0, 121.3018, 123.3700),
        ("2019-04-22", "10:52.2", 15.0, 122.5700, 123.7600),
        ("2019-04-23", "12:23.5", 14.0, 123.8300, 125.4400),
        ("2019-04-24", "10:15.0", 13.0, 124.5200, 125.0100),
        ("2019-04-25", "10:43.9", 12.0, 128.8300, 129.1500),
    ];
}
#![功能(分配器api)]
使用chrono::{Date,Duration,ParseError,NaiveTime};
使用chrono::offset::{Local,TimeZone};
使用绘图仪::前奏::*;
fn parse_datetime(t:&str)->Date{
地方的
.datetime_来自_str(&format!(“{}0:0”,t),%Y-%m-%d%H:%m”)
.unwrap()
.日期(
}
fn解析时间(t:&str)->结果{
从_str(&format!(&2020-01-01 0:{},t),%Y-%m-%d%H:%m:%S%.f)返回匹配的Local.datetime_{
Ok(date)=>Ok(date.time().signed_duration_since(NaiveTime::from_hms(0,0,0))),
Err(e)=>{println!(“{}”,e);Err(e)},
};
}
fn main()->结果{
让数据=获取_数据();
将root=BitMapBackend::new(“stock-example.png”(1024768))放入绘图区域();
根。填充(&白色)?;
出租(从日期到日期)=(
parse_datetime(&数据[0].0)-Duration::days(1),
parse_datetime(&data[data.len()-1].0)+Duration::days(1),
);
让y_min=parse_time(“9:30.0”).unwrap();
让y_max=parse_time(“13:00.0”).unwrap();
让mut chart=ChartBuilder::on(&root)
.x_标签_面积_尺寸(40)
.y\标签\面积\尺寸(50)
.caption(“Time”(“sans serif”,30.0)。转换为_font()
.构建笛卡尔2d(从日期到日期,y最小值y最大值)?;
图表.配置网格()
.light\u line\u样式(&白色)
.y_label_格式化程序(&|y|format!(“{:02}:{:02}”,y.num_minutes(),y.num_seconds()%60))
.x|u label_格式化程序(&|x|x.naive_local().to_string())
.draw()?;
chart.draw_系列(
数据.国际热核实验堆()
.map(| x | Circle::new((解析_datetime(x.0)、解析_time(x.1).unwrap()),5,BLUE.filled()),
)?;
好(())
}
fn获取_数据()->Vec{
返回vec[
("2019-04-18", "10:11.5", 16.0, 121.3018, 123.3700),
("2019-04-22", "10:52.2", 15.0, 122.5700, 123.7600),
("2019-04-23", "12:23.5", 14.0, 123.8300, 125.4400),
("2019-04-24", "10:15.0", 13.0, 124.5200, 125.0100),
("2019-04-25", "10:43.9", 12.0, 128.8300, 129.1500),
];
}

#![feature(allocator_api)]

use chrono::{Date, Duration, ParseError, NaiveTime};
use chrono::offset::{Local, TimeZone};
use plotters::prelude::*;

fn parse_datetime(t: &str) -> Date<Local> {
    Local
        .datetime_from_str(&format!("{} 0:0", t), "%Y-%m-%d %H:%M")
        .unwrap()
        .date()
}

fn parse_time(t: &str) -> Result<Duration, ParseError> {
    return match Local.datetime_from_str(&format!("2020-01-01 0:{}", t), "%Y-%m-%d %H:%M:%S%.f") {
        Ok(date) => Ok(date.time().signed_duration_since(NaiveTime::from_hms(0, 0, 0))),
        Err(e) => { println!("{}", e); Err(e) },
    };
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = get_data();
    let root = BitMapBackend::new("stock-example.png", (1024, 768)).into_drawing_area();
    root.fill(&WHITE)?;

    let (from_date, to_date) = (
        parse_datetime(&data[0].0) - Duration::days(1),
        parse_datetime(&data[data.len() - 1].0) + Duration::days(1),
    );

    let y_min = parse_time("9:30.0").unwrap();
    let y_max = parse_time("13:00.0").unwrap();

    let mut chart = ChartBuilder::on(&root)
        .x_label_area_size(40)
        .y_label_area_size(50)
        .caption("Time", ("sans-serif", 30.0).into_font())
        .build_cartesian_2d(from_date..to_date, y_min..y_max)?;

    chart.configure_mesh()
        .light_line_style(&WHITE)
        .y_label_formatter(&|y| format!("{:02}:{:02}", y.num_minutes(), y.num_seconds() % 60))
        .x_label_formatter(&|x| x.naive_local().to_string())
        .draw()?;

    chart.draw_series(
        data.iter()
            .map(|x| Circle::new((parse_datetime(x.0), parse_time(x.1).unwrap()), 5, BLUE.filled())),
    )?;

    Ok(())
}

fn get_data() -> Vec<(&'static str, &'static str, f32, f32, f32)> {
    return vec![
        ("2019-04-18", "10:11.5", 16.0, 121.3018, 123.3700),
        ("2019-04-22", "10:52.2", 15.0, 122.5700, 123.7600),
        ("2019-04-23", "12:23.5", 14.0, 123.8300, 125.4400),
        ("2019-04-24", "10:15.0", 13.0, 124.5200, 125.0100),
        ("2019-04-25", "10:43.9", 12.0, 128.8300, 129.1500),
    ];
}