Elixir Livebookでインタラクティブな開発環境を構築
Elixir Livebookとは
Elixir Livebook は、Elixir のためのインタラクティブなノートブック環境です。Jupyter Notebook に似た使い心地で、データ分析、機械学習、プロトタイピング、ドキュメント作成など、幅広い用途に活用できます。
Livebookの主な特徴
- リアルタイム実行: コードセルを実行すると即座に結果が表示される
- ビジュアライゼーション: グラフやチャートを簡単に描画
- リアクティブ: 変数が更新されると依存するセルが自動再実行
- 共有とコラボレーション: ノートブックを簡単に共有・公開可能
- Elixir エコシステムとの統合: Mix プロジェクトとの連携、依存関係の管理
- Phoenix LiveView ベース: Web ベースのインターフェース、リアルタイム通信
インストールとセットアップ
方法1: スタンドアロンアプリケーション
最も簡単な方法は、Livebook の公式サイトからスタンドアロンアプリケーションをダウンロードすることです。
macOS の場合:
brew install livebook
または、手動でダウンロード:
# macOS
curl -L https://livebook.dev/install/macos -o livebook
chmod +x livebook
./livebook server
方法2: Elixir から直接起動
Elixir がインストールされている場合:
# Elixir インストール(macOS)
brew install elixir
# Livebook を escript としてインストール
mix escript.install hex livebook
# Livebook 起動
livebook server
方法3: Docker
Docker を使う方法:
docker run -p 8080:8080 -p 8081:8081 --pull always livebook/livebook
環境変数を設定して起動:
docker run -p 8080:8080 -p 8081:8081 \
-e LIVEBOOK_PASSWORD="secret" \
-v $(pwd)/notebooks:/data \
--pull always livebook/livebook
初回起動
Livebook を起動すると、ブラウザが自動的に開き、認証トークンが表示されます。このトークンを入力してアクセスします。
livebook server
# 出力例:
# [Livebook] Application running at http://localhost:8080/?token=xxxxx
基本的な使い方
ノートブックの作成
- Livebook のホーム画面で「New notebook」をクリック
- ノートブックの名前を入力
- コードセルとマークダウンセルを追加して実行
コードセルの実行
# シンプルな計算
IO.puts("Hello, Livebook!")
result = 1 + 1
IO.inspect(result)
実行結果:
Hello, Livebook!
2
マークダウンセル
マークダウンセルを使ってドキュメントを作成:
# データ分析プロジェクト
このノートブックでは、売上データを分析します。
## データの読み込み
まずは CSV ファイルを読み込みます。
セルの依存関係
Livebook は自動的にセル間の依存関係を解析します:
# セル 1
name = "Alice"
# セル 2(セル1に依存)
greeting = "Hello, #{name}!"
IO.puts(greeting)
セル1を更新すると、セル2も自動的に再実行されます。
データ分析の実践
CSVデータの読み込みと処理
Mix.install([
{:explorer, "~> 0.8"},
{:kino_explorer, "~> 0.1"}
])
alias Explorer.DataFrame
alias Explorer.Series
# CSV ファイルの読み込み
df = DataFrame.from_csv!("sales_data.csv")
# データの確認
Kino.DataTable.new(df)
データの集計と変換
# 売上金額の合計を計算
total_sales =
df
|> DataFrame.select(["amount"])
|> DataFrame.to_series()
|> Map.get("amount")
|> Series.sum()
IO.puts("合計売上: ¥#{total_sales}")
# 商品カテゴリごとの売上集計
category_sales =
df
|> DataFrame.group_by(["category"])
|> DataFrame.summarise(total: sum(amount), count: count(amount))
|> DataFrame.sort_by(desc: total)
Kino.DataTable.new(category_sales)
データのビジュアライゼーション
VegaLite を使ってグラフを描画:
Mix.install([
{:kino_vega_lite, "~> 0.1"},
{:explorer, "~> 0.8"}
])
alias VegaLite, as: Vl
# 売上推移のグラフ
Vl.new(width: 600, height: 400)
|> Vl.data_from_values(DataFrame.to_rows(df))
|> Vl.mark(:line)
|> Vl.encode_field(:x, "date", type: :temporal)
|> Vl.encode_field(:y, "amount", type: :quantitative)
|> Vl.encode_field(:color, "category", type: :nominal)
# カテゴリ別売上の棒グラフ
Vl.new(width: 600, height: 400)
|> Vl.data_from_values(DataFrame.to_rows(category_sales))
|> Vl.mark(:bar)
|> Vl.encode_field(:x, "category", type: :nominal)
|> Vl.encode_field(:y, "total", type: :quantitative)
|> Vl.encode(:color, field: "category", type: :nominal)
機械学習の実装
Scholar による機械学習
Elixir の機械学習ライブラリ Scholar を使った例:
Mix.install([
{:scholar, "~> 0.2"},
{:explorer, "~> 0.8"},
{:nx, "~> 0.6"}
])
alias Scholar.Linear.LinearRegression
alias Explorer.DataFrame
alias Explorer.Series
# データの準備
train_data = DataFrame.from_csv!("train.csv")
x_train =
train_data
|> DataFrame.select(["feature1", "feature2", "feature3"])
|> DataFrame.to_columns()
|> Enum.map(fn {_name, series} -> Series.to_list(series) end)
|> Nx.tensor()
y_train =
train_data
|> DataFrame.pull("target")
|> Series.to_tensor()
# モデルの学習
model = LinearRegression.fit(x_train, y_train)
IO.puts("モデルの学習が完了しました")
IO.inspect(model)
# 予測の実行
test_data = DataFrame.from_csv!("test.csv")
x_test =
test_data
|> DataFrame.select(["feature1", "feature2", "feature3"])
|> DataFrame.to_columns()
|> Enum.map(fn {_name, series} -> Series.to_list(series) end)
|> Nx.tensor()
predictions = LinearRegression.predict(model, x_test)
IO.puts("予測結果:")
IO.inspect(predictions)
Nx によるニューラルネットワーク
Mix.install([
{:nx, "~> 0.6"},
{:axon, "~> 0.6"},
{:kino, "~> 0.12"}
])
# シンプルなニューラルネットワークの定義
model =
Axon.input("input", shape: {nil, 784})
|> Axon.dense(128, activation: :relu)
|> Axon.dropout(rate: 0.2)
|> Axon.dense(10, activation: :softmax)
# モデルの可視化
Axon.Display.as_graph(model, Nx.template({1, 784}, :f32))
# 学習の実行
trained_model =
model
|> Axon.Loop.trainer(:categorical_cross_entropy, :adam)
|> Axon.Loop.metric(:accuracy)
|> Axon.Loop.run(train_data, epochs: 10, compiler: EXLA)
IO.puts("学習完了")
インタラクティブUI
Kino による入力フォーム
# テキスト入力
name_input = Kino.Input.text("名前を入力してください")
# 入力値の取得
name = Kino.Input.read(name_input)
IO.puts("こんにちは、#{name}さん!")
# 数値入力
age_input = Kino.Input.number("年齢を入力してください", default: 25)
# セレクトボックス
category_input = Kino.Input.select("カテゴリを選択", [
{"電子機器", :electronics},
{"書籍", :books},
{"食品", :food}
])
動的なダッシュボード
Mix.install([
{:kino, "~> 0.12"},
{:kino_vega_lite, "~> 0.1"}
])
# リアルタイムグラフの作成
alias VegaLite, as: Vl
graph =
Vl.new(width: 600, height: 400)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "time", type: :temporal)
|> Vl.encode_field(:y, "value", type: :quantitative)
|> Kino.VegaLite.new()
Kino.render(graph)
# データを定期的に更新
for i <- 1..100 do
point = %{
time: DateTime.utc_now(),
value: :rand.uniform() * 100
}
Kino.VegaLite.push(graph, point)
Process.sleep(100)
end
フォームとボタン
# ボタンの作成
button = Kino.Control.button("実行")
# ボタンのイベント監視
Kino.listen(button, fn event ->
IO.puts("ボタンがクリックされました: #{inspect(event)}")
end)
Kino.render(button)
Mix プロジェクトとの連携
既存プロジェクトの読み込み
# Mix プロジェクトのパスを設定
Mix.install([],
path: "/path/to/your/project",
config_path: "config/config.exs"
)
# プロジェクトのモジュールを使用
alias MyApp.Accounts
alias MyApp.Repo
users = Accounts.list_users()
IO.inspect(users)
カスタム依存関係の管理
Mix.install([
{:phoenix, "~> 1.7"},
{:ecto, "~> 3.11"},
{:postgrex, "~> 0.17"},
{:httpoison, "~> 2.0"},
{:jason, "~> 1.4"}
])
データベース操作
Ecto によるデータベースクエリ
Mix.install([
{:ecto_sql, "~> 3.11"},
{:postgrex, "~> 0.17"},
{:kino, "~> 0.12"}
])
# データベース接続の設定
defmodule Repo do
use Ecto.Repo,
otp_app: :livebook,
adapter: Ecto.Adapters.Postgres
end
# Repo の起動
Repo.start_link(
hostname: "localhost",
username: "postgres",
password: "postgres",
database: "myapp_dev"
)
# スキーマの定義
defmodule User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
timestamps()
end
end
import Ecto.Query
# ユーザーの取得
users =
User
|> where([u], u.age > 18)
|> order_by([u], desc: u.inserted_at)
|> limit(10)
|> Repo.all()
Kino.DataTable.new(users)
Web スクレイピング
Req によるHTTPリクエスト
Mix.install([
{:req, "~> 0.4"},
{:floki, "~> 0.35"},
{:kino, "~> 0.12"}
])
# Webページの取得
{:ok, response} = Req.get("https://example.com")
html = response.body
# HTMLのパース
{:ok, document} = Floki.parse_document(html)
# タイトルの抽出
title =
document
|> Floki.find("title")
|> Floki.text()
IO.puts("ページタイトル: #{title}")
# リンクの抽出
links =
document
|> Floki.find("a")
|> Floki.attribute("href")
|> Enum.take(10)
Kino.DataTable.new(
Enum.map(links, fn link -> %{url: link} end)
)
ノートブックの共有と公開
ノートブックのエクスポート
Livebook は以下の形式でエクスポート可能:
- Live Markdown (.livemd): Livebook ネイティブ形式
- Elixir Script (.exs): 通常の Elixir スクリプトとして実行可能
- HTML: 静的HTMLとして共有
GitHub との連携
# ノートブックを Git リポジトリに保存
git add notebook.livemd
git commit -m "Add data analysis notebook"
git push origin main
Livebook Teams での共有
Livebook Teams(有料サービス)を使うと、チームでノートブックを共有・コラボレーション可能:
- リアルタイム共同編集
- アクセス制御
- スケジュール実行
- シークレット管理
実践的なユースケース
ログ分析ダッシュボード
Mix.install([
{:explorer, "~> 0.8"},
{:kino_vega_lite, "~> 0.1"}
])
# ログファイルの解析
logs =
File.stream!("app.log")
|> Stream.map(&String.trim/1)
|> Stream.reject(&(&1 == ""))
|> Enum.to_list()
IO.puts("ログ行数: #{length(logs)}")
# エラーログの抽出
error_logs =
logs
|> Enum.filter(&String.contains?(&1, "ERROR"))
|> Enum.take(10)
Kino.DataTable.new(
Enum.map(error_logs, fn log -> %{message: log} end)
)
APIテストと監視
Mix.install([
{:req, "~> 0.4"},
{:kino, "~> 0.12"}
])
# API エンドポイントのテスト
defmodule APIMonitor do
def check_endpoint(url) do
start_time = System.monotonic_time(:millisecond)
result = case Req.get(url) do
{:ok, %{status: 200} = response} ->
{:ok, response}
{:ok, response} ->
{:error, "Status: #{response.status}"}
{:error, error} ->
{:error, inspect(error)}
end
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
{result, duration}
end
end
# テスト実行
{result, duration} = APIMonitor.check_endpoint("https://api.example.com/health")
IO.puts("結果: #{inspect(result)}")
IO.puts("レスポンスタイム: #{duration}ms")
まとめ
Elixir Livebook は、インタラクティブな開発環境として非常に強力なツールです。主な利点:
- 即座のフィードバック: コードを書いてすぐに結果を確認
- ビジュアル化: データやグラフを美しく表示
- ドキュメント化: コードと説明を一体化
- 共有とコラボレーション: ノートブックを簡単に共有
- プロトタイピング: アイデアを素早く試せる
データサイエンス、機械学習、Web開発、システム監視など、幅広い用途で活用できるので、ぜひプロジェクトに導入してみてください。