LoginSignup
71
40

More than 1 year has passed since last update.

Rubyでグラフを描画するツール GR.rb の紹介

Last updated at Posted at 2019-12-12

image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png

これはなんの記事?

GRというグラフ描画ライブラリのRubyバインディングの記事です。

Rubyだってグラフを描きたいのです!

こんにちは。気がつくとRubyのコードをこちょこちょ書いているkojix2と申します。

 Rubyでグラフを描きたいって思ったことはありませんか? もちろんRubyにもグラフを描くツールはいくつかあります。たとえば

 どれも良いツールではあるのですが、一長一短で私は満足できませんでした。

 2019年の初め、rubyplotの開発が発表されました。これはRubyにふさわしいAPIをもつオブジェクト指向のグラフ描出用ライブラリということで、非常に期待していました。しかし開発者のみなさん忙しくてなかなか時間が取れないみたいで、開発は停滞。完成しなそうな雰囲気が漂ってきました。思えばRubyにふさわしいグラフ描画のためのAPIを一から考えて実装するのは簡単な仕事ではなかったのだと思います。

 ここ数ヶ月間、Ankaneさんが次々と機械学習のライブラリを発表しています。どうしてそんなことができるのかなと思って、プロジェクトの中身を観察してみると、ruby-ffiを利用していました。ffiを使えばC言語に詳しくなくてもバインディングを作成できるのではないかと思い、GR.rbの開発をはじめました。ネーミングは最初はffi-grだったのですが、文字が丸っこくて可愛いという理由でGR.rbを採用することにしました。

(GRではアニメーションの描出や、3Dの描出もできます! こういうのRubyではあんまり見ないでしょ?)

image.png gks.gif gks.gif gks.gif gks.gif gks.gif gks.gif gks.gif gks.gif gks.gif

(扉絵とアニメーションを描出するコードは全てExampleの中にあります。)

インストール

GRの最新のリリース版をダウンロードして解凍し、適当なディレクトリに配置します。
環境変数GRDIRを設定して、GR.rbにgrの場所を教えます。

export GRDIR=/your/path/to/gr

非公式ですがMacではHomebrewを用いてインストールすることもできます。(一部正常に動作しない場合があります。正常に動作しない理由はバージョンによってさまざまなのですが、GKS_WSTYPE="gksqt" など、環境変数でワークステーションを指定することによって改善する場合もあります。)

brew install libgr

Gemをインストールします。GR.rbというプロジェクト名なのですが、gemの名前はruby-grになっていますのでご注意ください。

gem install ruby-gr

WIndowsでは、gem install ruby-gr とするだけで自動的にMinGWからgrがダウンロードされてすぐに利用できるようになります。(しかし、こちらも非公式リリースなのでバージョンにより不具合が出たりします。)

簡単なつかい方

irbやpryに下のスクリプトをコピペしてみてください。GKSウィンドウが立ち上がります。
ファイルから実行する場合は、一瞬だけウィンドウが表示されて、スクリプトが実行し終わると瞬時に消えてしまいます。一番最後の行にgetssleep 10を入れてRubyのスクリプトが終了しないようにしてください。

線グラフ

require 'gr/plot'

x = [0, 0.2, 0.4, 0.6, 0.8, 1.0]
y = [0.3, 0.5, 0.4, 0.2, 0.6, 0.7]

GR.plot(x, y)

Macでの実行画面
image.png

ヒストグラム

実行には histogram gem が必要です。

require 'numo/narray'
require 'gr/plot'

data = Numo::DFloat.new(10_000).rand_norm
GR.histogram(data)

Ubuntuでの実行画面
image.png

塗りつぶした2次元等高線図

require 'numo/narray'
require 'gr/plot'
include Numo

x = 8 * DFloat.new(100).rand - 4
y = 8 * DFloat.new(100).rand - 4
z = NMath.sin(x) + NMath.cos(y)
GR.contourf(x, y, z)

Windowsでの実行画面
image.png

タイトル、軸ラベルなど

require 'rdatasets'
require 'gr/plot'

passenger = RDatasets.datasets.AirPassengers
time = passenger.at(0).to_a
value = passenger.at(1).to_a

opts = { title: 'Air Passenger numbers from 1949 to 1961',
         ylabel: "Passenger numbers (1000's)",
         xlabel: 'Date'
       }
GR.plot(time, value, opts)

Windowsでの実行画面
image.png

より詳しい使い方を知りたい場合は、Wikiをご覧ください。スクリーンショットに取り上げられているグラフはいずれもExampleに入っています。APIは今後も大きく変更になる可能性があります。

主なモジュールとクラス

GRとGR3という2つのモジュールから、grの関数を呼び出して使います。これらは細かくカスタマイズされたグラフの描出には便利です。けれども多くのケースではもっと簡単にグラフを描きたいでしょう。その場合はgr/plotを使います。require 'gr/plot' をすると、GRモジュールに簡単にグラフを描出するメソッドが追加されます。下にGRモジュールGR3モジュールPlotクラスについて簡単に説明します。

  • Plotクラス

    • 普段はこれを使う
    • `require 'gr/plot'
    • GRを上書き・置換して簡単にグラフを書けるメソッドを追加する。
    • plot step scatter stem histogram contour contourf hexbin heatmap nonuniformheatmap wireframe surface plot3 scatter3 imshow isosurface polar polarhist polarheatmap trisurf tricont shade volume (未実装あり)
    • Example その 1
    • Example その 2
    • 完成度はまだ未熟、大きな変更も入る
    • GR.jlに準拠。オブジェクト指向よりも関数的に。
  • GRモジュール

    • require 'gr'
    • モジュールメソッドはgrの関数を直接呼び出す
    • 主に2次元プロットや簡単な3次元プロットに使う
    • リファレンス [python] [julia] [ruby]
    • (Ruby版リファレンスは引数を省略しているのでJulia版・Python版もご参照ください)
  • GR3モジュール

    • require 'gr3'
    • モジュールメソッドはgr3の関数を直接呼び出す
    • 複雑な3D描写に使う
    • リファレンス [python] [julia] [ruby]
    • (Ruby版リファレンスは引数を省略しているのでJulia版・Python版もご参照ください)

Jupyterで使う

Jupyter + IRubyでも動作します。

image.png

そもそもGRってなに?

https://github.com/sciapp/gr
image.png

GRはユーリヒ総合研究機構Josef Heinenさん達が開発しているグラフ描画ライブラリです。GRはJuliaではPlotのデフォルトのバックエンドに採用されてます。PythonやR言語など特定の言語に依存しないグラフ描画ライブラリとしてはかなり有名な方じゃないかなと思います。Githubのコントリビューションを見ると、8年前から継続的な開発が続けられているのがわかります。
image.png

工夫したところ

Ankaneさんのプロジェクトのディレクトリ構成を参考にしました。ありがとうAnkaneさん。私がいろいろここに書くよりも、Ankaneさんのリポジトリを見るのが勉強になります。例えばlightgbmはコードの量も多くなくてオススメです。

具体的にはFFIモジュールを作って、そこにattach_functionでCの関数をRubyのメソッドとして登録します。Rubyといえばテストですが、Ankaneレポジトリを見ればffiを使うプロジェクトでtravis-ci等をどう設定すればいいかわかると思います。

私が工夫したところは、ブロック構文と継承を使って、RubyのArrayとpointerの相互変換を自動化したところです。(私はこれまでblock構文がよくわかっていなくて、はじめて満足がいく方法でブロックを使うことができた気がします。しかし、ひょっとするとこの書き方は、第三者が見た時にどうやって動作しているのかわかりにくくて、メンテナンス上はあまりよろしくないかもしれません。)

    def inqtext(x, y, string)
      inquiry [{ double: 4 }, { double: 4 }] do |tbx, tby|
        super(x, y, string, tbx, tby)
      end
    end

(同じメソッドがpython-grだとこうなる。Rubyの方がスッキリして見える気がする。)

def inqtext(x, y, string):
    tbx = (c_double * 4)()
    tby = (c_double * 4)()
    __gr.gr_inqtext(c_double(x), c_double(y), char(string), tbx, tby)
    return [[tbx[0], tbx[1], tbx[2], tbx[3]],
            [tby[0], tby[1], tby[2], tby[3]]]

それから、プログラミングに限った話ではありませんが、わからないことはいろいろと質問してみることも大事だなと感じました。英語版のstackoverflowやGithubのissue欄を使えば日本人だけでなく、外国の方に質問することができます。みらい翻訳で英文をこしらえてThanksと書くのを忘れなければ、たいていの場合親切に教えてくれます。

ffiでRubyからC言語の関数を呼ぶ

 ruby-ffiは非常に良くできていて、ほとんどの局面でうまく動作します。とくに今のruby-ffiはRubyInstaller2の方がメンテナンスしているので、Windowsの対応は抜群です。私はruby-ffiのことは結構好きです。けれども、ちょっと調べるとruby-ffiはかつてプロジェクトが死にかけた過去があるようです。ruby-ffiのリポジトリを眺めると、いくつかC言語のソースコードが含まれているのがわかります。私はC言語わかりませんが、Rubyの仕様が大きく変更になった時にruby-ffiを追従させていくのは結構な労力がいるんじゃないかなと推測します。

ruby-ffiからfiddleへの移行について

 Rubyからffiを使用するライブラリはruby-ffiのほかに、fiddleがあります。こちらはRubyチームが管理しているので、Rubyの仕様が変更になっても安定して更新されると期待されます。けれどもfiddleは仕様がシンプルすぎて、ユーザーが使うのは正直しんどくて、普及しないのも仕方ないという感じもします。

 今回はfiddleへの移行にあたりfiddleyというモジュールを使うことにしました。fiddleyはfiddleを使ってruby-ffiと同じ使用感を実現するツールです。でもfiddleyは未完成なので、実際にfiddleyを使うときは自分でいくつかコードを追加・改変していく必要がありました。

 そのほか、Windowsでfiddleを使う際に、パスを通すだけでは共有ライブラリ(dll)が読み込まれません。実はRubyInstaller.add_dll_directoryを呼ばなければならないという注意点があります。ruby-ffiではこのへんは自動でうまくやってくれているようです。詳しくは下記のリンクをご覧ください。

Windows の Ruby の fiddle で lib○○.dll が読み込めない時、何をチェックすればよいでしょうか?

GR.rbのこれから

red-data-toolsのプロジェクトになりました

 もともと使いやすいRubyのグラフ描画ライブラリが見当たらないこととから自分のために作りはじめたGR.rbですが、個人のプロジェクトなのでなかなかユーザーも増えず発展は難しいだろうなと思っていました。そんな時にred-data-toolsのプロジェクトにしないかとお声がかかり、いつもお世話になっているので参加することにしました。プロジェクトを更新する権限が自分にしかないと、不測の事態で自分がいなくなった時に、その時点でプロジェクトが終了してしまう可能性もあるので、それを防止するためにもいいかなと思いました。

issue報告求めています

ここまでGR.rbの報告をしてきました。たくさんスクリーンショットも貼り付けてきたのですが、実際には開発は始まったばかりで、たくさんのバグや実装上の課題が残っています。GR.rbのメンテナンスは、これから最低でも5年、できれば10年単位で継続していきたいなと思っていて、見つかったバグは少しずつ除去していきたいと思っています。なのでもしも不具合が見つかったら、GithubのGR.rbのページからissueやプルリクエストをして頂けると嬉しいなと思います。

2019年を振り返って

Rubyはオブジェクト指向言語であり、関数のかわりにメソッドが多用されます。そのため、データ処理には使いにくいという意見を持っておられる方も少なくないと思います。しかし今年一年間の、Rubyとデータ処理まわりを振り返ってみると、非常に多くの進歩がありました。

  • 機械学習ライブラリRumaleのブレイク
  • AnkaneさんによるPyTorchLightGBMのバインディングの公開
  • Numo NArrayが本格的に海外で知名度を獲得しはじめた
  • ApacheArrowの開発が進んだ

ほかにも、4日目のアドベントカレンダーのうなぎおおとろさんがRuby用のdeep learningライブラリruby-dnnを作ってくれたりして、地味ながらも着実な成長が感じられる1年だったと思います。

来年のみなさまの生活もきっと豊かなものでありますように。

この記事は以上です。

71
40
13

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
71
40