1. はじめに
StreamlitだとPlotlyのマウスホバー・クリック情報を活用したインタラクティブ可視化が難しそうだったので(詳しい方は情報いただけるとありがたいです)、Plotly Dashを勉強中です。
Dashでマルチページを作成したくて、色々調べたので備忘録です。
見栄えのよいナビゲーションバーをつけるために、Bootstrapを利用しました。
2. 参考にしたページ
3. 開発環境
- macOS Ventura
- Miniconda
- Python==3.11.6
- Dash==2.14.2
- Plotly==5.9.0
- dash-bootstrap-components==1.5.0
4. ライブラリインストール
% conda install -c conda-forge plotly
% conda install -c conda-forge dash
% conda install -c conda-forge dash-bootstrap-components
5. ディレクトリ構成
dash_multipage
├── app.py
└── pages
├── home.py
├── page1.py
├── page2.py
└── page3.py
6. 動作イメージ(非公式)
使用に際してのイメージです。正確ではない可能性があります。
7. .py
ファイルの内容
データにはPlotlyで取得できるデータセットを使用しました。
使用したデータセットはiris
、gapminder
、wind
です。
import dash
from dash import Dash, html, dcc
import dash_bootstrap_components as dbc
app = Dash(__name__,
external_stylesheets=[dbc.themes.SANDSTONE],
use_pages=True)
navbar = dbc.Nav(
children=[dbc.NavItem(dbc.NavLink(page["name"],
href=page["relative_path"],
class_name="nav-item")) for page in dash.page_registry.values()],
id = "navibar",
class_name = "navbar navbar-expand-lg bg-dark"
)
app.layout = dbc.Container([
dcc.Location(id='url', refresh=False),
navbar,
html.Br(),
dash.page_container
])
if __name__ == '__main__':
app.run(debug=True)
BootstrapテンプレートにはSandStoneを利用しました。
クラス名は上のリンク先を参照しました。
dash.register_page(__name__)
を宣言すると、layout
変数に代入したレイアウトに従って、app.py
のdash.page_container
の場所に配置されるようです。どの.pyファイルを参照するかは、dcc.Location
で定義してるっぽいです(id='url'
)が、まだちゃんと理解しきれていないので、引き続き調べる予定です。
import dash
from dash import html
dash.register_page(__name__, path='/')
layout = html.Div([
html.H1('Dash Multipage Test'),
html.Br(),
html.Div([html.H3("Page1: Iris Data"),
html.H3("Page2: Gapminder Data"),
html.H3("Page3: Wind Data"),
])
])
import dash
from dash import html, dcc, callback, Input, Output
import plotly.express as px
from plotly.data import iris
import json
page_number = "page1"
data = iris()
dash.register_page(__name__)
layout = html.Div([
html.H2("Scatter Plot of Iris data"),
html.Div([dcc.Graph(id=f"{page_number}_scatter",
figure=px.scatter(data,
x='sepal_length',
y='sepal_width',
color='species'))]),
html.Br(),
html.Div([html.P(id=f"{page_number}_json")])])
@callback(Output(f"{page_number}_json","children"),
[Input(f"{page_number}_scatter","hoverData")])
def scatter_plot_hover_data(hover_data):
if hover_data is None:
return ""
point_data = hover_data['points'][0]
point_data_json = json.dumps(point_data)
return point_data_json
ページの内容としては、Iris
データのsepal
の長さと幅をそれぞれx
、y
として散布図を描き、マウスホバーした点の情報をjson
形式で記述するものとなっています。
シングルページの場合との違いは、callback
をapp
オブジェクトのメソッドとして記述するのではなく、callback
関数をdash
からインポートしている点です。
参考ページにある通り、callback
で使う各ページファイル内のidが被っているとエラーで動かないので、ページごとにidを変える必要があります。
page2.py
とpage3.py
の内容はpage1.py
とほとんど同じなので割愛します。
8. 結果
最終的に、こんな感じになりました。
なかなか悪くない感じです。