Livebook 事始め (Elixir)

**Livebook** は Elixir版 **Jupyter Notebook** と言われているものです。私も **Python** や **Julia** で **Jupyter Notebook** を少しだけ使ったことがあり、ちょっとした利用には大変便利だと感じていたので、**Livebook** も試してみました。

すこし驚いたのが、**Livebook** は **Phoenix LiveView** のアプリケーションだということです。とても安定して動いているし、機能も結構豊富だということです。特に**VegaLite** や **Kino** を使って高度なグラフを描くことができるのは大変魅力的です。

**関連記事**
[VegaLite で埼玉県を切る( Elixir, Livebook) - Qiita](https://qiita.com/sand/items/a8b99ee8b68f49b5784a)
[Vega-Lite の View Composition (Elixir, Livebook) - Qiita](https://qiita.com/sand/items/f0e11ed907287be6436a)
[VegaLite の基礎 (Elixir, Livebook) -Qiita](https://qiita.com/sand/items/3433061b1966d6f8edb2)
[Livebook 事始め (Elixir) - Qiita](https://qiita.com/sand/items/6753c1782774b45daf6b)

この記事は **「東京電力電力供給状況監視 - Livebook」** というタイトルにすべきものでしたが、記事の趣旨が **Livebook** の使用感にあるため表記のタイトルとしました。
[東京電力電力供給状況監視 - Phoenix LiveView](https://qiita.com/sand/items/ecac700bc3e32f106279)


# 1. VegaLite と Kino について 

## Livebook
**Livebook** は Elixir版 **Jupyter Notebook** です
* [Livebook 公式サイト](https://github.com/livebook-dev/livebook)
* [Livebookをインストール、起動する](https://qiita.com/nako_sleep_9h/items/585786648d2b7560d010)


::: note info
docker で動かす
docker run -p 8080:8080 -p 8081:8081 --pull always livebook/livebook
:::


:::note info
**Livebook のインストール**
> git clone https://github.com/livebook-dev/livebook.git
> cd livebook
> mix deps.get --only prod

**Windows コマンドプロンプトでの起動**
> set MIX_ENV=prod
> mix phx.server
:::


:::note warn
起動エラー時に試してみること
> iex --sname foo
:::



## VegaLite
**VegaLite** は **Vega-Lite** の **Elixir bindings** です。
**Vega-Lite** is ハイレベルな、会話的グラフィックのための文法です。（D3.jsは低レベル）。それは宣言的な **JSON** 文法を使います。**VegaLite**　でも **JSON** 文法を使うことができるようですが、主に Elixir の文法が使われます。

* [VegaLite](https://hexdocs.pm/vega_lite/VegaLite.html)
* [Vega-Lite](https://vega.github.io/vega-lite/docs/)


## Kino
**Kino** は **Livebook** で使われるライブラリで、Elixir コードを使って直接リッチでインタラクティブな描画を可能とするものです。
**Kino** は単独でも使われますが、**Kino.VegaLite** という形で **VegaLite** とともに使われることが一般的です。
しかし **Kino v0.5.2** までは **Kino.VegaLite** がドキュメントにあるのですが、**Kino v0.6.0** 以降は消えています。代わりに以下の別パッケージを使うみたいです。（名前にドットが入っていないだけなので勘違いしそうですが。）関数の説明などは **Kino v0.5.2** の方を参照した方が良いです。
* [**KinoVegaLite**](https://hexdocs.pm/kino_vega_lite/components.html)
* [**Kino v0.5.2**](https://hexdocs.pm/kino/0.5.2/Kino.html)


# 2. Livebook のコード

必要なパッケージをインストールします。
```elixir:
Mix.install([
  {:vega_lite, "~> 0.1.6"},
  {:kino_vega_lite, "~> 0.1.4"},
  {:httpoison, "~> 1.8"} 
])

alias VegaLite, as: Vl
```


**VegaLite** と **Kino** でグラフを描きます。グラフは5分おき(300000 ms)に自動更新されます。
```elixir:
chart =
  Vl.new(width: 600, height: 300)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "x", type: :quantitative )
  |> Vl.encode_field(:y, "y", type: :quantitative, scale: [domain: [3100,3400]] )
  |> Kino.VegaLite.new()
  |> Kino.render()


Kino.VegaLite.periodically(chart, 300000, 0, fn i ->
  denki =
    case HTTPoison.get("http://tepco-usage-api.appspot.com/quick.txt") do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> body
      {:ok, %HTTPoison.Response{status_code: 404}} -> "0,0,0"
      {:error, %HTTPoison.Error{reason: _reason}} -> "0,0,0"
    end
  [_, y, _] = String.split(denki, ",")
  point = %{x: i, y: String.to_integer(y)}
  Kino.VegaLite.push(chart, point)
  {:cont, i + 1}
end)
```

コードは以上なのですが、**驚くほど短い**です

# 2. 動作画像
コードとグラフが同時にキャプチャーできるなんて、Livebook っぽくてかっこいいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/3349c32d-f00c-965e-b9d8-aef9eeb2ae43.png)

#  3. Kino や VegaLite 関数の説明 
### [new(opts \\\\ [])](https://hexdocs.pm/vega_lite/VegaLite.html#new/1)
**VegaLite struct** でラッピングされた新しい **specification** を返します。


### [Kino.VegaLite.new(vl)](https://hexdocs.pm/kino/0.5.2/Kino.VegaLite.html#new/1)
引数の **VegaLite** 定義で **widget process** をスタートします。

### [Kino.render(term)](https://hexdocs.pm/kino/0.5.2/Kino.html#render/1)
引数の **term** を render します。 

### [encode_field(vl, channel, field, opts \\\\ [])](https://hexdocs.pm/vega_lite/VegaLite.html#encode_field/4)
Adds **field encoding** を **specification** に追加します。
```elixir:
  |> Vl.encode_field(:y, "y", type: :quantitative, scale: [domain: [3100,3400]] )
```

ここでは **opts** として **type:** オプションと **scale:** オプションを指定しています。


### [**Kino.VegaLite.periodically(widget, interval_ms, acc, fun)**](https://hexdocs.pm/kino/0.5.2/Kino.VegaLite.html#periodically/4)
**KinoVegaLite** のドキュメントには明記されてませんが、**Kino.VegaLite v0.5.2** のドキュメントには以下の関数があり、そのまま **KinoVegaLite** でも使えるみたいです。
* **widget process** を定期的に走らせるための **callback** を登録する関数
* **callback** は **interval_ms ** ミリ秒ごとに走る
*  そして **accumulated value** を受け取る
*  **callback** 以下の2つのうちどれかをリターンしなければならない
    *　**{:cont, acc}** - 新しい **accumulated value** で **callback** を続ける
    *　**:halt** - もはや **callback** のスケジュールを停止する



今回は以上です。
コード自体は、本当に驚くほど短くて、素晴らしいプラットフォームだと感じました。しかし **VegaLite** の機能が豊富すぎるし、独特なので、少なからぬ試行錯誤を要しました。ドキュメントを読みこなす慣れが必要です。