🦀

Rustのデータ可視化ライブラリCharmingに入門した

2024/06/12に公開

Charmingとは

Charmingは、Rustの強力なチャートレンダリングライブラリです.美しく高品質なデータ可視化します.GitHub上で以下のようなグラフが紹介されています.

後述しますが,このCrateは適切にStructが分割されているので,可読性の高い可視化用のコードを作ることができます.

https://github.com/yuankunzhang/charming

簡単に散布図を作成する

Datasaurusのデータを使います.ここでは,csvファイルをダウンロードして使っています.
https://www.openintro.org/data/index.php?data=datasaurus

もちろん表示するのは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を使っていますが,HtmlRendererWasmRendererなどの他の出力方法にも対応しています.※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に多くのテンプレートが記載されています.

https://github.com/yuankunzhang/charming/tree/main/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を立てて作者に報告しています.
https://github.com/yuankunzhang/charming/pull/59

補足

※1: 内部でJSのランタイムを動かしているのでWebに対応しているのはそれはそうという気もしたので補足.以下 ImageRendererの例
https://github.com/yuankunzhang/charming/blob/f2dcfcd6ee7711b1d9484bbc4eda0515e2da50a2/charming/src/renderer/image_renderer.rs#L39

※2: JetBrainsであれば,Ctrl+Enterを使ってジャンプができる.ドキュメントで探しても良いが,ChartRenderer であれば,初手でgallery/srcのコードをコピペして動かした後に,そのコードからジャンプして直接読んだ方が早い気がしている.

Discussion