Flask + jQuery + Plotly でサーバーサイドの計算結果を動的にやり取りする

# 概要

Flask + jQuery + Plotlyでサーバーサイドで計算した結果を動的に可視化します。
Pythonの優位性であるデータ処理の利便性を活かせるので、簡易的なフレームワークで分析システムのプロトタイプを作成することに役立っています。

- Flask
軽量なフレームワークですがその分追加ライブラリの開発が盛んです。
今回はhtmlの描画と、GETに対し計算を行いjsonを返す部分を機能として持ちます。

- jQuery
Flaskで用意した入り口に値を投げて対応する処理を行いjsonを受け取り、それをパースしてPlotlyで\<div>に描画します。

- Plotly
Pythonの高機能な描画ライブラリです。

# 内容

今回はhtml側から値（平均、分散）を送り、サーバー側でそれに基づく正規分布を生成し、JavaScriptで動的に書き換えます。

各言語間のやりとりの流れは次のようになります。html -> JavaScript -> Python -> JavaScript -> html 

![vis.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257113/98d642f9-e94d-0e2d-2ed5-f808a0016d99.gif)


# 実装

## html

```{html: index.html}
    <!-- Form -->
    <p> Mean = <input type=text size=5 name=mean>
        Var = <input type=text size=5 name=var>
    <p><a href=# id=visualize>Visualize</a>

    <!-- Graph -->
    {% for id in ids %}
    <h3>{{id}}</h3>
    <div id="{{id}}"></div>
    {% endfor %}
```

html側では更新しない為にhrefに#、動作を検知するためにidに名前、後で書き換えるために\<div>をid付で用意します。

## JavaScript 

```{JavaScript: visualize.js}
$('a#visualize').bind('click', function() {
    $.getJSON($SCRIPT_ROOT + '/_calc_dist', {
      mean: $('input[name="mean"]').val(),
      var: $('input[name="var"]').val()
    }, function(data) {
      // Recieve data from python as json and json.dumped.
      var ids = data.ids;
      var graphs = JSON.parse(data.graphJ);

      // Plot by plotly.
      Plotly.newPlot(ids[0],
                  graphs[0].data,
                  graphs[0].layout || {});
    });
    return false;
  });
```

JavaScript側ではid=visualizeのクリックを検知して、Flaskで用意したルーティングに値を投げ、その結果を受け取ってパースしてPlotlyに渡します。


## Python

```{Python: main.py}
# API for calculation.
@app.route('/_calc_dist')
def calc_dist():
    # Make Gaussian data.
    mean = request.args.get('mean', 0, type=float)
    var = request.args.get('var', 0, type=float)
    x = np.linspace(mean - 10, mean + 10, 1000)
    y = stats.norm.pdf(x=x, loc=mean, scale=np.sqrt(var))
    graphs = make_graph_data(x, y)

    # Convert data to json
    graphJSON = json.dumps(graphs, cls=plotly.utils.PlotlyJSONEncoder)
    ids = ['graph-{}'.format(i) for i, _ in enumerate(graphs)]
    return jsonify(graphJ=graphJSON, ids=ids)

@app.route('/', methods=["GET", "POST"])
def index():
    return render_template('index.html', ids=ids)

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=18888, threaded=True)
```

Python側ではrequestで値を受け取り、statsでそれに対応した確率分布を計算してPlotly形式のJsonにしてJsonifyで返します。make_graph_dataはPlotly用のデータを作成するために個別実装した関数です。
idsはリストなのでJavascript側でパースは必要ないですが、graphJSONはjsonとしているのでパースが必要な点だけ注意が必要です。

```{Python}
def make_graph_data(x, y):
    graphs = [
        dict(
            data=[
                dict(
                    x=x,
                    y=y,
                    type='scatter'
                ),
            ],
            layout=dict(
                title='Interactive Gaussian',
                xaxis=dict(range=[min(x), max(x)])
            )
        )
    ]
    return graphs
```

# まとめ
サーバーサイドにPythonを使うとデータの操作を簡単に出来るので便利です。やり取りもJsonなので親和性が高く実装も簡単で、またPlotlyという高機能なライブラリもそれらを後押ししてくれます。

ソースコードは下記で公開しています。

- [ソースコード](https://github.com/NibuTake/flask/tree/master/plotly-js)

# References
- [Flask + Plotly](https://github.com/plotly/plotlyjs-flask-example)
- [Flask + Ajax](https://www.for-engineer.life/entry/flask-ajax/)