python製データ可視化ツール Dashを触ってみた所感

# はじめに

データ収集を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

```python
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であれば、

```python

import dash_core_components as dcc

dcc.Markdown('''

# 見出し
## 見出し2
本文
''')  

```

マークダウンも記述できるのでこちらを使った方が良さそう
https://dash.plot.ly/dash-core-components/markdown

# Graphs

https://dash-gallery.plotly.host/Portal/

```python
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コンポーネントに値を渡すことで棒グラフを簡単に作成できる

### 表

```python
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
```python

@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パーツを紐づけるというところが肝で、

```python

#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は一つだけに限定される


```python

@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も処理したい
ような場合もあるので、設計をうまくやらないと

```python
@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判定を行なっている

```python

@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についても、解決法があれば順次追記していきたい。