Elixirでグラフ描画?
現状、いい感じのライブラリがない。
Matplotlibのラッパー( https://github.com/MaxStrange/expyplot )はあるようだが、それはMatplotlib。
最近はラッパーではないElixir純正のものを作ろうとしている人もいるみたいだが( https://github.com/elcritch/plotex )、まだまだ当面の間使える感じではなさそう。
諸々調べてみるとPlotly.jsがイケてる感じで、elixirのラッパーが作りやすそうだったので作ってみた。
Plotly.js とは
ブラウザでグリグリ動かせる系のインタラクティブなグラフ描画ライブラリ。
https://plot.ly/javascript/
pythonやら何やらでもplotlyは使うことができるが、javascript版はオープンソース化されて100%無償利用可能になったのですごい。
(pythonでもofflineでプロットすれば無償で利用できて、onlineでAPIを叩く感じでプロットしようとすると課金が必要になる場合がある?この辺はあまり詳しくない。)
今回はElixirでPlotly.jsを使うためのラッパーを作ったので、100%無償で利用できる。
Plotly.ex とは
今回私が作った薄いラッパー。
https://github.com/s417-lama/plotly_ex
「薄い」というのは、Plotly.jsがほぼjsonを突っ込むだけでplotできるため。
Plotly.exでやっているのはmapをjsonに変換して(これもJasonでやるだけなのだが)それをPlotly.jsに突っ込むだけ。
使い方
だいたい
https://github.com/s417-lama/plotly_ex
ここに書いてあるが、例として上とは違う例を紹介する。
下のように書くとグラフがプロットされ、ブラウザに勝手に表示される。
versions = [1.0, 2.03, 3.0, 95, 98, 2000, 7, 8, 10]
[%{
x: Enum.map(versions, fn ver -> "Windows #{ver}" end),
y: versions,
type: "bar",
}]
|> PlotlyEx.plot(%{width: 700, height: 500})
|> PlotlyEx.show
このように素晴らしいグラフがElixirで簡単に作れてしまう。
ちなみに「Save as SVG」をクリックするとSVGファイルで保存可能。
上のElixir scriptはただ単にelixir
コマンドで実行しようとしても、当然dependencyがないのでエラーになる。
pythonみたいな感じで適当にスクリプトファイルを書いてプロットしたければ(わざわざmix new
とかしたくなければ)erunを使うと良い。
以下はerunの紹介。
Qiita: https://qiita.com/s417-lama/items/d543121d14a86ec841ca
GitHub: https://github.com/s417-lama/erun
Mix dependencyとしては
def deps do
[
{:plotly_ex, git: "https://github.com/s417-lama/plotly_ex.git"},
]
end
を指定。
APIの説明
現在はAPIは2つだけ提供している。
PlotlyEx.plot(data, layout \\ %{})
data
とlayout
を受けとり、プロットするdivのHTMLをstringで返す。
data
とlayout
はjsonに変換されてPlotly.jsのPlotly.plot
関数にそのまま渡される。
なので、data
とlayout
の指定方法はPlotly.jsの公式を参照。
公式ドキュメント: https://plot.ly/javascript/
iex> PlotlyEx.plot([%{type: "scatter", x: [1, 2], y: [3, 4]}]) |> IO.puts
<div class="plotly-ex">
<div id="plotly-ex-body-771"></div>
<script>
Plotly.plot('plotly-ex-body-771', [{"type":"scatter","x":[1,2],"y":[3,4]}], {})
</script>
</div>
こんな感じでHTMLが返ってくる。
PlotlyEx.show(plot_html, opts \\ [])
PlotlyEx.plot/2
で生成したHTML stringを受け取り、ブラウザ上にグラフを表示する。
オプション:
opts :: [filename: string()]
filename:
が指定されていたら、HTMLは指定のパスに保存されてそのファイルがブラウザ上で開かれる。
filename:
が指定されていない場合(デフォルト)、使い捨てのウェブサーバを立ち上げてブラウザにそれを開かせる。
iex(3)> PlotlyEx.plot([%{type: "scatter", x: [1, 2], y: [3, 4]}]) |> PlotlyEx.show
listening on http://localhost:39865
accepted. quitting...
:ok
使い捨てのウェブサーバというのは一回レスポンスを返したら終了するだけのウェブサーバ。
扱いの面倒くさい一時ファイルを避けるためにこうなっている。
PlotlyEx.show/2
が呼ばれるとワンタイムサーバを立ち上げて(上の例ではhttp://localhost:39865
。portは適当に選ばれる)、ブラウザ上でそこにアクセスさせる。
ブラウザにレスポンスを返した時点でウェブサーバは終了し、処理が返る。
ブラウザを開かせるためにはopen
かxdg-open
コマンドを走らせていて、デフォルトのブラウザが使用される。
環境によっては動かないのだが、少なくとも自分の環境では動いているのであまり気にしていない。
(動かなかったらissueを立てておいてください。)
Phoenixと組み合わせる
Step 1. Plotly.exをdepsに追加
def deps do
[
{:plotly_ex, git: "https://github.com/s417-lama/plotly_ex.git"},
]
end
Step 2. Plotly.jsをHTMLヘッダに追加
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
例えばlib/hoge_web/templates/layout/app.html.eex
に。
Step 3. テンプレートエンジンとか(例えばlib/hoge_web/templates/page/index.html.eex
)で
<%=
PlotlyEx.plot([
%{
x: ["AVX 2", "AVX 512"],
y: [2, 512],
type: "bar",
}
])
|> raw()
%>
おわりに
基本的には自分が使うために作ったので自分好みの仕様になっています。
使いにくい・イケてないと感じたら、ぜひ
https://github.com/s417-lama/plotly_ex
にissueを立てるか、PRを送るかしてもらえると助かります。
erunと組み合わせることで使い捨てのplot用スクリプトが簡単に書けるようになった。
これでElixirでもpythonみたいに気軽にplotして遊べるようになった。はず。