3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GR.cr を使ってグラフを少しだけ描く話

Last updated at Posted at 2021-06-25

 Crystal言語を使ってグラフを描く方法は、そんなにない。

 最近、Cryplot というのが出て、ようやく多少Gnuplotでグラフを表示できるようなった。

 私は1-2年前からGR.rbという名前のRubyのGRバインディングを作成しており、GRのGRMという機能を利用することで、比較的簡単に本格的なグラフが描けることがわかっていた。しかし、reddit等で呼びかけても誰も作る様子がないので、仕方がないのでgr-crystalをフォークして自分で作った。

 自分で作ったといっても、無から作ったわけではなく、多くの他の人の力の上にコードを書いており、自分が作った部分はそんなに多くない。まず第一にGRMの仕組みがチートすぎる。C言語のバインディングが作成できるプログラミング言語ならば、だいたいなんでも綺麗なグラフが描けてしまう。それから、Jun Makino氏が gr-crystal 作成されていたので最初からGRバインディングの土台があった。モジュールの構成に大きな変化を加えるため、プルリクエストという形でgr-crystalの開発に参加するよりも、最初から別のリポジトリとして開発することを選択した。

 当初は c2ffi というヘッダーファイルから、jsonファイルを作成するソフトを利用して、バインディングを自動生成していがが、実はそんなことをする必要はなかった。Crystalには非常に優秀な自動バインディング作成ツールが存在していて、これを使うと本当に作業が楽だった。

 さて、まだまだ完成には程遠いので、なんとなく描けるようになったグラフを張っていく。

clifford_attractor.cr

コード量が少ないのに美しいグラフが描出できるため、満足度が高い。

require "../src/gr"

n = 100_000_000
x0 = 0.0; y0 = 0.0
a = -1.3; b = -1.3; c = -1.8; d = -1.9; dθ = 0.007

x = [x0]
y = [y0]
θ = 0.007

n.times do |i|
  x << (Math.sin(a * y[i]) + c * Math.cos(a * x[i])) * Math.cos(θ)
  y << (Math.sin(b * x[i]) + d * Math.cos(b * y[i])) * Math.cos(θ)
  θ += dθ
end

GR.setviewport(0, 1, 0, 1)
GR.setwindow(-3, 3, -3, 3)
GR.setcolormap(8)
GR.shadepoints(x, y, 5, 480, 480) # NOTE: use dims: xform: ?
GR.updatews

gets

shadepoints の右側に変なコメントが入っているのは、GR.rbに合わせてAPIを変えるかもしれないのでメモである。

これを実行するとこんな画像が表示される。

image.png

とても綺麗である。そしてとにかく速い。手元の環境では、Rubyはおろか、Juliaよりも高速という状況であった。(Crystalは実行速度の速さに定評の有る言語で、Crysatl, Nim, Juliaなど新しい言語でベンチマークを取ると、Crystalが一番速いという結果が出ることが多い。一方で、コンパイルの遅さにも定評のある言語で、コンパイル時間では遅い結果が出てしまうことが多い。。)

GRM

次はGRMを利用するケースである。こんな感じである。

require "../src/grm"

n = 1000
x = [] of Float64
y = [] of Float64
z = [] of Float64
n.times do |i|
  x << i * 30.0 / n
  y << Math.cos(x[i]) * x[i]
  z << Math.sin(x[i]) * x[i]
end

plot_types = %w[line hexbin polar shade stem step
  trisurf plot3 scatter scatter3]

plot_types.each do |type|
  GRM.clear
  args = GRM.args_new
  GRM.args_push(args, "x", "nD", n, x)
  GRM.args_push(args, "y", "nD", n, y)
  GRM.args_push(args, "z", "nD", n, z)
  GRM.args_push(args, "kind", "s", type)
  GRM.args_push(args, "title", "s", type)
  GRM.plot(args)
  sleep 2
end

するとこんな感じのグラフが表示される。

Scatterがわかりにくいが一応ちゃんと描出されている。

 これは例であって、GRMは histogram や、surface などもっと多様なグラフを描出できる。かなり便利である。GR.crは作りかけなので時間が空いているときに気が向いたら作っていくつもりである。

ライセンスはMITにしているので、フォークして勝手に自分のプロジェクトにしてさらに改良してもらって全然構わない。私も暇な時間が無限にあるわけではないので、便利なグラフ描出ソフトを開発できる方がいるならそれはそれで大歓迎である。もちろん可能だったらオープンソースにして他の人もソースが利用できるようにしていただきたい。

また、はじめに書いたように、GRMの仕組みを使えばC言語のバインディングが作れる言語であれば、かなり容易に高品質なグラフ描出ライブラリが作成できる(C言語の可変長引数に対応している必要あり)。もしもマイナーな言語を利用していて、まだ良いグラフ描出ライブラリがない場合はおすすめである。

この記事は以上です。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?