plot - 如何绘制 x 轴上的日期和 y 轴上的时间的系列?
问题描述
我想绘制点,其中 x 坐标是日期,y 坐标是时间(如NaiveTime
来自 Chrono)。
我认为这将是使用Plotters板条箱最简单的方法。不幸的是,默认情况下它不支持 y 轴上的 NaiveTime,如GitHub 问题所示。但是,https://plotters-rs.github.io/book/basic/basic_data_plotting.html?highlight=date#time-series- chart 的文档指出
理论上,绘图仪支持任何数据类型为轴。唯一的要求是实现轴映射特征。
所以听起来不错。
因此,我改编了他们的Stock.rs示例,并得到以下内容。
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),
];
}
结果:
error[E0277]: the trait bound `std::ops::Range<NaiveTime>: plotters::prelude::Ranged` is not satisfied
--> src/main.rs:47:49
|
47 | .build_cartesian_2d(from_date..to_date, y_min..y_max)?;
| ^^^^^^^^^^^^ the trait `plotters::prelude::Ranged` is not implemented for `std::ops::Range<NaiveTime>`
现在,如何实现 Trait。...除了在https://docs.rs/plotters/0.3.0/src/plotters/coord/ranged1d/types/datetime.rs.html#600中似乎已经实现。所以,我们可以做
.build_cartesian_2d(from_date..to_date, RangedDateTime(y_min, y_max))?;
失败了
52 | .build_cartesian_2d(from_date..to_date, RangedDateTime(y_min, y_max))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `RangedDateTime { 0: val, 1: val }`
因此,我们更改https://docs.rs/plotters/0.3.0/src/plotters/coord/ranged1d/types/datetime.rs.html#581以使结构字段公开:pub struct RangedDateTime<DT: Datelike + Timelike + TimeValue>(pub DT, pub DT);
。它失败了
52 | .build_cartesian_2d(from_date..to_date, RangedDateTime(y_min, y_max))?;
| ^^^^^^^^^^^^^^ the trait `ranged1d::types::datetime::TimeValue` is not implemented for `NaiveTime`
但是,我们不能实现TimeValue
for NaiveTime
,因为这是当前的定义:
pub trait TimeValue: Eq {
type DateType: Datelike + PartialOrd;
// ...
}
而NaiveTime
不是DateLike
。我不知道如何从这里开始,我考虑了以下选项:
- 添加
TimeLike
到关联类型的限制TimeValue
, DateLike
从 的关联类型中删除TimeValue
,- 添加一个单独的特征
DateTimeValue
,它可以TimeLike
作为它的DateType
.
解决方案
在这种情况下,它更容易Duration
从 Chrono 中使用,因为 Plotters 也有一个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),
];
}
推荐阅读
- javascript - 在 POST 上使用 JQuery 创建双下拉 SelectList 时返回 0。ASP.Net CORE 2.1
- java - Java Scanner 返回 null 但已过滤掉
- matlab - 使用 Matlab 查找函数使用其坐标获取顶点的索引
- javascript - 尝试设置随机重定向主页?
- selenium - 有没有办法改变 selenium/node-chrome 图像 chromedriver.exe 版本?
- python - 在 html 中搜索颜色标签 (Python 3)
- vb.net - 检查函数调用中哪个数字更大 - vb.net
- java - 列表
和 java.lang.ClassCastException: com.google.gson.internal.StringMap - java - 通过构造函数的依赖注入不适用于 EJB bean
- unity3d - 如何正确设计巨型世界中游戏对象的生成?