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

# 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%無償利用可能になった](https://plot.ly/javascript/open-source-announcement/)のですごい。
（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](https://github.com/michalmuskala/jason)でやるだけなのだが）それをPlotly.jsに突っ込むだけ。

## 使い方
だいたい
https://github.com/s417-lama/plotly_ex
ここに書いてあるが、例として上とは違う例を紹介する。

下のように書くとグラフがプロットされ、ブラウザに勝手に表示される。

```elixir
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/703e1822-64e5-ddf5-550c-5d1ad8fae685.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としては

```elixir
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/

```elixir
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:`が指定されていない場合（デフォルト）、使い捨てのウェブサーバを立ち上げてブラウザにそれを開かせる。

```elixir
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に追加

```elixir
def deps do
  [
    {:plotly_ex, git: "https://github.com/s417-lama/plotly_ex.git"},
  ]
end
```

**Step 2.** Plotly.jsをHTMLヘッダに追加

```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`）で

```elixir
<%=
  PlotlyEx.plot([
    %{
      x:    ["AVX 2", "AVX 512"],
      y:    [2, 512],
      type: "bar",
    }
  ])
  |> raw()
%>
```
こうする。
![plotly_avx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/446d6b11-7ede-169e-f7c1-9bd1a5d233b6.png)
こうなる。

# おわりに

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

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

[erun](https://github.com/s417-lama/erun)と組み合わせることで使い捨てのplot用スクリプトが簡単に書けるようになった。
これでElixirでもpythonみたいに気軽にplotして遊べるようになった。はず。