ジェネリクスに手を出してみた。ヒストグラムを描く関数の中でいちいち型を明記して実装しなければいけないのがなんとなく嫌だったので汎用的に使えるヒストグラム描画関数を作ろうとしたのが発端だった。
元のヒストグラム描画はこんな感じだった。
fn draw_histogram(df: DataFrame) -> Result<DataFrame> {
let root = BitMapBackend::new("histogram.png", (640, 480)).into_drawing_area();
root.fill(&WHITE).unwrap();
let lidar = df.column("lidar").expect("could not find `lidar` column");
let lidar_max: i64 = lidar.max().unwrap();
let lidar_min: i64 = lidar.min().unwrap();
let mut chart = ChartBuilder::on(&root)
.x_label_area_size(35)
.y_label_area_size(40)
.margin(5)
.caption("Lidar Histogram", ("sans-serif", 30))
.build_cartesian_2d((lidar_min..lidar_max).into_segmented(), 0i64..5000i64)
.expect("could not prepare chart");
chart
.configure_mesh()
.bold_line_style(&WHITE.mix(0.3))
.y_desc("Count")
.x_desc("Lidar")
.axis_desc_style(("sans-serif", 15))
.draw()
.unwrap();
chart
.draw_series(
Histogram::vertical(&chart)
.style(BLUE.minx(0.5).filled())
.data(lidar.i64().unwrap().into_iter().map(|x| (x.unwrap(), 1))),
)
.unwrap();
Ok(df)
}
この実装だと、受け取れるのはDataFrameだけだし、lidarというカラムだけしか扱えないし、lidarというカラムはi64でないといけないし、と問題ばかりなので、ヒストグラムを描く機能から分離したい。というわけで実装を変えてみた。
fn draw_vec_histogram<T>(
series: &Vec<T>,
name: &str,
caption: &str,
xlabel: &str,
ylabel: &str
) {
let max: T = *series.iter().max().unwrap();
let min: T = *series.iter().min().unwrap();
// 各値の頻度を数えたい
let mut appearance = std::collections::HashMap::new();
series.iter().for_each(|x| {
*appearance.entry(x).or_insert(0) += 1;
});
let ymax = appearance.iter().map(|(_, v)| *v).max().unwrap();
let root = BitMapBackend::new(name, (640, 480)).into_drawing_area();
root.fill(&WHITE).unwrap();
let mut chart = ChartBuilder::on(&root)
.x_label_area_size(35)
.y_label_area_size(40)
.margin(5)
.caption(caption, ("sans-serif", 30))
.build_cartesian_2d(max..min, 0..ymax)
.expect("could not prepare chart");
chart
.configure_mesh()
.bold_line_style(&WHITE.mix(0.3))
.y_desc(ylabel)
.x_desc(xlabel)
.axis_desc_style(("sans-serif", 15))
.draw()
.unwrap();
chart
.draw_series(
Histogram::vertical(&chart)
.style(BLUE.minx(0.5).filled())
.data(*series.into_iter().map(|x| (x, 1))),
)
.unwrap();
}
とここまで実装してみたが、int, uint, floatに対応できるようなTの境界の与え方は色々調べてみたがよくわからなかった。一体どう実装すればいいんだろう・・・