標準入力からヒストグラムを描画するCLIツールを作った

標準入力をいい感じにヒストグラムにするCLIツールを作りました。

GitHub - genya0407/hist

インストール

Releases · genya0407/hist · GitHub からお好きなバイナリをダウンロードして、適当なパスに展開してください

使い方

なんかこういう感じのテキストファイルがあるとする。

$ cat example.txt
4.486107060301375
4.400612185880518
3.1836054290123
1.8814038706949097
3.367418962291763
2.5550752855238943
2.7646969681590603
4.099374705165457
4.765991107086257
3.1929965581891215
...(略)

これをhistコマンドの標準入力に食わせると、以下のようなAAが出力される。

cat example.txt | hist
466.1|
468.3|
470.5|
472.7|
474.9|**
477.1|**
479.3|****
481.4|********
483.6|*************
485.8|************************
488.0|******************************
490.2|******************************************
492.4|*****************************************************
494.6|*******************************************************************
496.8|**********************************************************************
499.0|********************************************************************************
501.1|***************************************************************************
503.3|*************************************************************************
505.5|*****************************************************************
507.7|*******************************************************
509.9|**************************************
512.1|***********************************
514.3|*******************
516.5|***************
518.6|*********
520.8|*****
523.0|**
525.2|*
527.4|
529.6|
531.8|
     +--------------------------------------------------------------------------------+ 995 times
     +----------------------------------------+ 497 times

縦軸が「値」で、横軸が「頻度」です。上の例だと、500ぐらいの値が出る頻度が一番高くて、その頻度は1000回ぐらいであることがわかります。

ちなみに、横軸の高さを調整するオプションや、値をグルーピングする単位(いわゆる bin)を調整するオプションもあります。

contributionは大歓迎です。あと、入力の行数が少ないときにバグっぽい挙動をするので注意してください。

モチベーション

アクセスログとかDBの中身をヒストグラムにしたいことは稀によくある。例えば「ユーザーあたりのブログポスト数ってどういう分布になってるのかな?」みたいなのを知りたいなど。

継続的に見ていきたいメトリクスならGrafanaとかのキチンとしたツールで見たほうが良いと思うんですが、今この瞬間サッと見れるだけでいいんだけど、というニーズも少なからずあります。そういうときにいちいちjupyter notebookを立ち上げて、seabornの書き方を調べながら、フォント環境が壊れててイライラしたり、gnuplotでいい感じにヒストグラムを書こうとして四苦八苦するのは不毛です*1

見たい対象のデータがどこにあるのかによっても違ってきますけど*2アクセスログをテキストファイル形式で扱う場合や、MySQLにクエリを打ってデータ取ってくるときは、シェルからデータが生まれてくるわけで、シェルのなかでグラフ描画までできたら楽でいいですよね。

というわけで、「シェルから生まれてきた数字列をそれっぽいヒストグラムAAに起こす」という処理を例によってRubyワンライナーで毎度毎度書いてたんですが、ちゃんとしたヒストグラムを書くのは意外にめんどくさいのでCLIツールにしちゃおと思ったのが、今回histコマンドを作ろうと思ったきっかけです。

メイキング

Rustの良いところ

CLIツールがいい感じに作れてすごかった(小並感)。

この argopt ってcrateがめっちゃ便利。

tanakh.hatenablog.com

詳しくは上のリンクを読んでもらったら良いんですけど、例えば以下のように書いてコンパイルして実行すると、

use argopt::cmd;

#[cmd]
fn main(
    #[opt(short = "b", long = "bin")] bin: Option<f64>,
    #[opt(short = "l", long = "bar-length", default_value = "80")] bar_length: i64,
) {
  // 略
}

以下のような出力が得られます。

$ hist --help
hist 0.1.0

USAGE:
    hist [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -l, --bar-length <bar-length>     [default: 80]
    -b, --bin <bin>      

もちろんmain関数の引数にはいい感じに引数が渡ってきます。これはちょっと感動的に便利ですよね。 今回RustでCLI作ろうと思ったきっかけの1/4ぐらいは、argoptの紹介記事を読んだことです。

余談ですが、最新のバージョン(v0.1.1)のargoptは、dependencyとしてargoptを指定するだけじゃ動かなくて、structoptも指定してあげないとダメっぽかったので、issueを立てて報告しました(英語が拙い)。

github.com

Github Actionsがべんり

これもすごかった。

motemen.hatenablog.com

この記事を真似するだけで、

  • タグをpushすると
  • macOS/Linux/Windows用バイナリのビルドが走り
  • 各種プラットフォーム向けのバイナリ一覧を含んだリリースが作成される

という魔法みたいな状態を構築することができます。当然ビルドされたバイナリは展開してPATHに置くだけで実行できる。シングルバイナリ最高です。今まで僕がRubyCLI作って配布方法をウンウン悩んでたのは何だったんだろう...

まとめ

標準入力からヒストグラムを描画するCLIツールをRustで作りました。RustはCLIツールを作成・配布する上でとても楽な選択肢であり、オススメです。

*1:もちろん、使い慣れてる人ならそんなに大変じゃないとは思いますけど、僕はあんまり使い慣れてないので...

*2:例えば、elasticsearchにアクセスログが入ってるケースとかは、histコマンドで取り扱うのには不適切かも。素直にkibanaを導入したほうが良さそう。