Rustのデータ可視化ライブラリCharmingに入門した
Charmingとは
Charmingは、Rustの強力なチャートレンダリングライブラリです.美しく高品質なデータ可視化します.GitHub上で以下のようなグラフが紹介されています.
後述しますが,このCrateは適切にStructが分割されているので,可読性の高い可視化用のコードを作ることができます.
簡単に散布図を作成する
Datasaurusのデータを使います.ここでは,csvファイルをダウンロードして使っています.
もちろん表示するのはdinoです.
この記事では,cargo new project_name
した後に,data
dirとoutput
dirを作っていることを想定します.
$ls
Cargo.lock Cargo.toml README.md data output src target
src
の中身はこんな感じです.(中身はこれから作ります)
$tree src
src
├── datasaurus.rs
├── main.rs
└── scatter.rs
1 directory, 3 files
データの読み込みをする関数の作成
Datasaurusはこのようなデータセットになっています.datasetはdinoだけでないことに注意してください.
dataset | x | y |
---|---|---|
dino | 55.3846 | 97.1795 |
dino | 51.5385 | 96.0256 |
dino | 46.1538 | 94.4872 |
csv
crateを使ってもいいのですが,楽をしたいので polars
を使います.PolarsはAPIが使いやすく,さらに,ドキュメントの検索がしやすいのでオススメです.
use polars::prelude::*;
pub fn datasaurus(file_path: &str) -> Result<Vec<Vec<f64>>> {
// CSV ファイルを読み込み DataFrame にする
let df: DataFrame = CsvReader::from_path(file_path)?
.has_header(true)
.finish()?;
// 'dataset' column が 'dino' である行のみに行を絞る
let dino_df: DataFrame = df.filter(&df.column("dataset")?.equal("dino")?)?;
// x, y columns を Vec<Vec<f64>> にキャストしてreturnする
let x_col: &Float64Chunked = dino_df.column("x")?.f64()?;
let y_col: &Float64Chunked = dino_df.column("y")?.f64()?;
let mut result: Vec<Vec<f64>> = Vec::new();
for (x, y) in x_col.into_iter().zip(y_col.into_iter()) {
if let (Some(x), Some(y)) = (x, y) {
result.push(vec![x, y]);
}
}
Ok(result)
}
散布図を描く関数の作成
Charmingでは以下のように,Chart
というStructをまず作成して,その後にRenderer
で出力します.今回はImageRenderer
を使っていますが,HtmlRenderer
,WasmRenderer
などの他の出力方法にも対応しています.※1
use charming::{
component::{
title::Title,
axis::Axis,
},
element::{TextStyle, Padding},
series::Scatter,
theme::Theme,
Chart,
ImageRenderer,
ImageFormat,
};
pub fn scatter_plot(
data: Vec<Vec<f64>>,
output_path: &str
)
{
let chart = Chart::new()
.title(
Title::new()
.text("The Datasaurus")
.text_style(
TextStyle::new()
.font_size(32)
.font_weight("bold")
)
.padding(Padding::Double(10.0, 100.0))
)
.x_axis(Axis::new())
.y_axis(Axis::new())
.series(
Scatter::new()
.symbol_size(16)
.data(data)
);
ImageRenderer::new(1080, 720)
.theme(Theme::PurplePassion)
.save_format(
ImageFormat::Png,
&chart,
output_path
).expect(
"レンダリングに失敗しました"
);
}
Chartを作る側,出力する側で適切に分担されているおかげで,非常に可読性の高いコードを作成することができます.
データの可視化ライブラリでは,テンプレートがないと何ができるかわからないという問題が発生しがちですが,Charmingではgallery/src
に多くのテンプレートが記載されています.
初手では,gallery/src
で作りたい図表を出力するコードを読み,エディタなどでジャンプしながらAPIを探すと良いと思います.※2
以下はRustRoverのスクリーンショットです.
main関数の作成
Datasaurusを読み込み,そのデータをscatter_plot
関数に渡す.
use crate::datasaurus::datasaurus;
mod scatter;
mod datasaurus;
fn main() {
let dino_data: Vec<Vec<f64>> = datasaurus("data/datasaurus.csv").unwrap();
scatter::scatter_plot(dino_data, "output/datasaurus.png");
}
コードを作成したら,以下を実行します.
cargo run
output/datasaurus.png
に図が出力されれば成功です.
まとめ
Rustでデータの前処理をすることを考えていて,合わせて使うCrateを探していたところ,Twitterで提案があり,試してみました.美しい表を可読性の高いコードで簡単に作れるところが魅力だと感じました.興味が湧いた方は是非一度使ってみてください.
注意 (解消済み)
2024.06.15
追記:
チュートリアルにバグがあったのですが,以下のPull Requestで解消されました.🎉
GitHubのチュートリアルのコードに注意してください.以下の部分のImageFormat::PNG
は実際にはImageFormat::Png
です.
https://github.com/yuankunzhang/charming#renderers
この件に関しては,PullRequestを立てて作者に報告しています.
補足
※1: 内部でJSのランタイムを動かしているのでWebに対応しているのはそれはそうという気もしたので補足.以下 ImageRenderer
の例
※2: JetBrainsであれば,Ctrl+Enter
を使ってジャンプができる.ドキュメントで探しても良いが,Chart
やRenderer
であれば,初手でgallery/src
のコードをコピペして動かした後に,そのコードからジャンプして直接読んだ方が早い気がしている.
Discussion