7
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 5 years have passed since last update.

[Plotly.ex] Elixirでもグラフをプロットしたい!

Posted at

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

plotly_windows.png

このように素晴らしいグラフが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 \\ %{})

datalayoutを受けとり、プロットするdivのHTMLをstringで返す。
datalayoutはjsonに変換されてPlotly.jsのPlotly.plot関数にそのまま渡される。
なので、datalayoutの指定方法は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は適当に選ばれる)、ブラウザ上でそこにアクセスさせる。
ブラウザにレスポンスを返した時点でウェブサーバは終了し、処理が返る。

ブラウザを開かせるためにはopenxdg-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()
%>

こうする。
plotly_avx.png
こうなる。

おわりに

基本的には自分が使うために作ったので自分好みの仕様になっています。

使いにくい・イケてないと感じたら、ぜひ
https://github.com/s417-lama/plotly_ex
にissueを立てるか、PRを送るかしてもらえると助かります。

erunと組み合わせることで使い捨てのplot用スクリプトが簡単に書けるようになった。
これでElixirでもpythonみたいに気軽にplotして遊べるようになった。はず。

7
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
7
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?