「血圧を記録するアプリを作る」シリーズです
前回は「AIを使って血圧計の写真を元に値を入力する」でした
今回は「A血圧測定の結果を元にグラフを作る」を作ります
実行環境
- OS Ubuntu 24.04
- GPU RTX4090(今回はなくても動く)
- Elixir 1.17.1-otp-27
- Erlang 27.2.1
- Phoenix 1.8.2
- Olama 0.13.0 (今回はなくても動く)
- モデル gemma3:27b(今回はなくても動く)
今回の実行イメージ
前提知識
VegaLite
過去の僕の検証
公式
プログラムを書く
vega_liteとvega_lite_convertを追加
グラフを描くライブラリです
mix.exs
defmodule BloodPressureRecord.MixProject do
use Mix.Project
# 省略 #
defp deps do
[
# 省略 #
+ {:vega_lite, "~> 0.1.11"},
+ {:vega_lite_convert, "~> 1.0.1"}
]
end
# 省略 #
ライブラリーを取得
$ mix deps.get
ルータ追加
/graphを追加
lib/blood_pressure_record_web/router.ex
defmodule BloodPressureRecordWeb.Router do
# 省略 #
pipeline :api do
plug :accepts, ["json"]
end
scope "/", BloodPressureRecordWeb do
pipe_through :browser
get "/", PageController, :home
live "/blood_pressures", BloodPressureLive.Index, :index
live "/blood_pressures/new", BloodPressureLive.Form, :new
live "/blood_pressures/:id", BloodPressureLive.Show, :show
live "/blood_pressures/:id/edit", BloodPressureLive.Form, :edit
live "/up", BloodPressureLive.UploadLive, :index
+ live "/graph", BloodPressureLive.Graph, :index
end
# 省略 #
lib/blood_pressure_record_web/live/blood_pressure_live/index.exをコピーして作ります
lib/blood_pressure_record_web/live/blood_pressure_live/graph.ex
defmodule BloodPressureRecordWeb.BloodPressureLive.Graph do
use BloodPressureRecordWeb, :live_view
alias BloodPressureRecord.BloodPressures
alias VegaLite, as: Vl
alias VegaLite.Convert, as: VlConvert
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash}>
<img src={"data:image/png;base64,#{@blood_pressures_png}"}>
</Layouts.app>
"""
end
解説
- render
- 画像を描画します
- imgタグにbase64形式で画像データを渡します
lib/blood_pressure_record_web/live/blood_pressure_live/graph.ex
@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:page_title, "Listing Blood pressures")
|> assign(:blood_pressures_png, blood_pressures_png())}
end
defp blood_pressures_png() do
BloodPressures.list_blood_pressures()
|> Enum.map(&convert_data/1)
|> List.flatten()
|> draw_graph()
end
def convert_data(data) do
date = NaiveDateTime.to_date(data.measured_at)
[
%{date: date, type: "最高血圧", value: data.systolic},
%{date: date, type: "最低血圧", value: data.diastolic},
%{date: date, type: "脈拍", value: data.pulse}
]
end
def draw_graph(data) do
# 1. グラフ化するデータの定義 (ロングフォーマット)
# 日付(date), 測定項目(type), 値(value) を持たせる
# 2. Vega-Liteの定義
Vl.new(width: 600, height: 400)
# データをグラフに渡す
|> Vl.data_from_values(data)
# マークは折れ線のみを使用
# point: trueでデータ点も表示し、見やすくする
|> Vl.mark(:line, point: true)
# X軸のエンコーディング: 日付は時系列データとして扱う
|> Vl.encode_field(:x, "date",
type: :temporal,
title: "測定日",
axis: [
format: "%Y/%m/%d",
tickCount: "day"
]
)
# Y軸のエンコーディング: 値をマッピング
|> Vl.encode_field(:y, "value", type: :quantitative, title: "値 (mmHg または 拍/分)")
# 色のエンコーディング: type(測定項目)ごとに色分けして、線を分ける
|> Vl.encode_field(:color, "type", type: :nominal, title: "測定項目")
# グラフの書き出し
|> VlConvert.to_png()
|> Base.encode64()
end
end
解説
-
mount
- ページを開いた時に実行されます
- blood_pressures_pngでグラフの画像を作ります
-
blood_pressures_png
- list_blood_pressuresでDBからデータを取得
- convert_dataでグラフで扱える用にデータを変換
- draw_graphでグラフを描く
-
draw_graph
- 細かい内容はコメントを参照
- VlConvert.to_pngでpng形式に変換
- Base.encode64()でBase64形式に変換
Webサーバーを起動
$ mix phx.server
http://localhost:4000/graph アクセスしてください
グラフ画面を表示できます
ソース(各回共通のため更新されます)
おしまい
