はじめに
データ収集をpython、pandasで行うProjectを進めているのですが、
python製のダッシュボード作成ツールを探していたところdashにたどり着いた。
以下はdash検証とサンプル作成時に得た知見のまとめと備忘録
Dashとは
plotly謹製のwebアプリケーション作成ツール
Flaskを元にしており、pythonで書くとReact.jsに変換される
https://plot.ly/dash/
データをグラフィカルに表示するコンポーネントが多数用意されており、
用意にデータが可視化できるところが特徴
例)
https://dash-gallery.plotly.host/Portal/
Html Elements
app.layout = html.Div(children=[
html.H1(id='elm1', className='hoge' children=[]),
#略
html.Div(id='elm2', className='hoge' children=[]),
]
htmlコンポーネントが用意されており、
https://dash.plot.ly/dash-html-components
app.layoutに渡すことで描画される。
構造が入れ子になるとchildren=に入れた子要素のchildrenにさらに子要素を...
といった形で複雑化しそうなので部分部分でコンポーネントした方が良いかも
部分的に静的なhtmlであれば、
import dash_core_components as dcc
dcc.Markdown('''
# 見出し
## 見出し2
本文
''')
マークダウンも記述できるのでこちらを使った方が良さそう
https://dash.plot.ly/dash-core-components/markdown
Graphs
dcc.Graph(
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
Graphコンポーネントに値を渡すことで棒グラフを簡単に作成できる
表
import pandas as pd
dataframe = pd.read_csv('url')
dash_table.DataTable(
id='table',
columns=[ {"name": i, "id": i} for i in dataframe.columns],
hidden_columns=[],
row_selectable="single",
data=dataframe.to_dict('records'),
),
dictionaryやpandasのdataframeから表を作成できる
Callback
@app.callback(
Output('table', 'data'),
[Input('region-dropdown', 'value')])
def update_table(value):
return df.query('市区町村名 == @value').to_dict('records')
UIパーツのInputとOutputを紐付けすることでUIコールバックが発火できる。
UIパーツをが必要不可欠
UIパーツを紐づけるというところが肝で、
#app.layout
dcc.Location(id='location', refresh=False),
@app.callback(
[Output('hoge', 'value1'),
Input('location', 'pathname')])
def pass_change(value1, pathname):
urlパスの変化を受け取るのにもdcc.LocationというUIパーツを置いて
そのコールバックとして変化値を受け取らなければならない
Outputは1つだけの法則
Dashにおいて、callbackのoutputは一つだけに限定される
@app.callback(
Output('hoge', 'children'),
Input('fuga', 'value2'))
def func(value2):
@app.callback(
Output('hoge', 'children'),
[Input('fuga', 'value'),
Input('hoyo', 'value')
])
def func(value1, value2):
上記のように、hogeをoutputとするcallbackが重複していた場合、
DuplicateCallbackOutput
You have already assigned a callback to the output
An output can only havea single callback function.
上記のように怒られてしまう。
Outputに対してInputをまとめていく必要があるが、Inputに対して別のOutputも処理したい
ような場合もあるので、設計をうまくやらないと
@app.callback(
[Output('hoge', 'children'),
Output('hoge', 'children'),
Output('hoge', 'children'),
Output('hoge', 'children'),
Output('hoge', 'children'),
],
[Input('fuga1', 'value'),
Input('fuga2', 'value'),
Input('fuga3', 'value'),
Input('fuga4', 'value'),
Input('fuga5', 'value'),
])
多数のInputに対して多数のOutputが集まった巨大なCallbackになってしまうので注意が必要
ButtonのCallback
buttonのclickイベントを取れないか調査したところ、
過去にはEventクラスがあったが現在はremoveされているとのこと。
buttonクラスのpropertyにはn_clicksというものがあり、
公式の例を見るとボタンを押すたびにn_clicksが増えていく
https://dash.plot.ly/dash-core-components/button
全てのボタンのn_clicksをキャッシュしないと押されたことが分からないのは辛い
ということでpropertyを探すとn_clicks_timestampというものがあったため、
以下のようにtimestampと現在の時刻を比較してclick判定を行なっている
@app.callback(
Output('hoge', 'children'),
Input('close-button', 'n_clicks_timestamp')])
def click(n_clicks_timestamp):
if n_clicks_timestamp != None:
if (time.time() * 1000 - n_clicks_timestamp) < 1000:
return something
これが本当に正しいやり方なのかイマイチ自信が持てないので、
Dashに詳しい人教えてください
所感
使用感に多少の癖はあるが、表やグラフが非常に少ないコード量で記述できるのは魅力的
今回挙げたconsについても、解決法があれば順次追記していきたい。