トップページに戻る

plotly/Dash/Flaskメモ

詳細は左の▶をクリック: Webアプリケーション、CGI、フレームワーク

 Webアプリケーションは、CGI (Common Gate Interface) と呼ばれるプログラムをHTTPサーバから呼び出して実行されます。
CGIプログラムはどの言語で書いても構いませんが、以下のインターフェースを持ちます。

また、HTML出力に、HTML5やCSS5、Javascriptを使うことで、動的・インターラクティブなアプリケーションを作成することが可能です。

 しかしながら、上記のようなプログラムをHTML/CSS/Javascriptを使って作成するには多くの知識とプログラムコードが必要となり、現在では、単純なWebアプリを除き、CGI形式が使われることは少なくなりました。上記の機能は、"フレームワーク"と呼ばれるWebアプリ用ライブラリにまとめられ、複雑なHTML/CSS/Javasciptのコードを隠ぺいし、簡単にプログラムを作成できるようなっています。

 本ページでは、pythonを使ってWebアプリを作る際のメモをまとめます。pythonのフレームワークとしてはDjangoが有名ですが、多機能である一方、習得に必要な学習コストが大きいという問題があります。一方で、Flaskは軽量で比較的簡単に習得可能であり、データ解析などのダッシュボードとして利用されるDashはFlaskで動いています。Plotlyはpythonの可視化ライブラリの一つで、matplotlibよりもインタラクティブなグラフを描くことができますが、Flask/Dash上で稼働するWebアプリとして利用さるように作られています。一方、FastAPIは最新のフレームワークの一部であり、軽量・高速という特長があり、今後は主流になっていくものと考えられます。

 上記のことを考え、本ページでは以下の項目を扱います。


Plotlyプログラム例

注意: .run_server()をdebugモードで走らせるとスクリプトが2重起動 (リロード) される。
    ログファイルを出力するときなどはdebugモードをoffにする。

  1. plot_excel_show.py
    .show()を使ってplotlyのグラフをweb browserに表示する例 (Dashは使っていない)
    Usage: python plot_excel_show.py
     

  2. plot_excel_base.py
    これ以下はPlotly Dashの例。
    Excelファイルをアップロードし、Plotlyで描画する
    Usage: python plot_excel_base.py
     を実行した後、コンソールに表示されたURLにアクセスする。
    あるいは
    Usage: python start_web_app.py plot_excel_base.py 127.0.0.1 8050
     

  3. plot_excel_xrange.py
    Excelファイルをアップロードし、Plotlyで描画する。表示X範囲をinput boxで設定し、ボタンを押してグラフを再描画する
    Usage: python start_web_app.py plot_excel_xrange.py 127.0.0.1 8050
      

  4. plot_excel_xrange_auto.py
    Excelファイルをアップロードし、Plotlyで描画する。
    グラフの表示範囲を変えるとinput boxのX範囲に設定される。
    input boxのX範囲を変更するとグラフを再描画する
    Usage: python start_web_app.py plot_excel_xrange_auto.py 127.0.0.1 8050
      

  5. plot_excel_details.py
    plot_excel_base.py に HTMLの折り畳み要素 <details> とテーブルを入れたもの
    Usage: python start_web_app.py plot_excel_details.py 127.0.0.1 8050
     

  6. plot_tkProg.py
    必要ライブラリ: tklib
    tklib.tkapplication とstart_web_app.pyの機能を統合 (Windows限定)。
    sever起動のスクリプトは別のコンソールで実行するようにした。
    .run_server()のdebug modeをデフォルトでoffにした。
    Usage: python plot_tkProg.py


Plotlyをつかったpythonプログラムの作成法

 一般的なWebフレームワークでは、フレームワークライブラリにHTTPサーバが用意されており、まずHTTPサーバを起動し、表示されたURLにアクセスすることでアプリを実行します。

 Plotlyでは、plotly.graph_objects を利用して .show() を実行することにより、Plotly内部でHTTPサーバの立ち上げとアプリの実行を行ってくれるため、matplotlibと同様の使い方をすることができます。

 以下のプログラム plotly_demo1.py を実行すると、 http://127.0.0.1:53763/ を起動してグラフを表示しました。

import numpy as np
import plotly.graph_objects as go


xs = np.linspace(0, 10, 100)
sins = np.sin(xs)
randoms = np.random.rand(100)

fig = go.Figure(data=[
    go.Scatter(x=xs, y=sins, name="sin"),
    go.Scatter(x=xs, y=randoms, name="random"),
    ])
fig.show()


Plotly Dashをつかったpythonプログラムの作成法

 次の例では、pythonプログラムは .run_server()を呼び出し、アクセスURLを表示したうえでHTTPサーバを起動します。

from dash import Dash, dcc, html, Input, Output
import plotly_express as px

app = Dash(__name__)

app.layout = html.Div([
html.H4('Analysis of Iris data using scatter matrix'),
dcc.Dropdown(
    id="dropdown",
    options=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'],
    value=['sepal_length', 'sepal_width'],
    multi=True
    ),
    dcc.Graph(id="graph"),
    ])


@app.callback(
    Output("graph", "figure"), 
    Input("dropdown", "value"))
    def update_bar_chart(dims):
    df = px.data.iris() # replace with your own data source
    fig = px.scatter_matrix(df, dimensions=dims, color="species")
    return fig

app.run_server(debug=True)

このプログラムを実行すると、コンソールに以下の表示が出ます。

PS D:\> python .\plotly2.py
Dash is running on http://127.0.0.1:8050/

* Serving Flask app 'plotly2'
* Debug mode: on

Webブラウザを起動してhttp://127.0.0.1:8050/ にアクセスすると、以下のようなページが表示されます


Dashのcallback

上記のPlotly Dashプリでは、上段のドロップボックスを変更することで、グラフの表示を変えることができます。
ドロップボックスの選択を変更した場合、callback関数を呼び出しますが、Dashではcallback関数の定義が高度にカプセル化されています。
また、Flask/Dashでは、callback関数の定義では、decolatorが使われています。

@app.callback(
    Output("graph", "figure"), 
    Input("dropdown", "value"))
    def update_bar_chart(dims):
        df = px.data.iris()
        fig = px.scatter_matrix(df, dimensions=dims, color="species")
        return fig

  1. @app.callbackでdecolateされた関数はcallbackとして認識されます。
  2. 引数には Output(), Input(), Status()の3種類を受け取ります。
  3. Input()の第一引数のId "dropdown" に対応するWebコンポーネントの "value" (第二引数) の値が変更されると、
    @app.callback() update_bar_chart(dims) が呼び出されます。
  4. @app.callback() update_bar_chart() はIput()、Status()の値を引数として受け取り、Output()に渡す値を戻します
  5. update_bar_chart()からの返り値は、Output()の第一引数のId "graph" に対応するWebコンポーネントの "figure" (第二引数) 属性に設定され、Webページが更新されます

pythonのデコレータ

 pythonでは、@hogehoge というデコレータを関数 func の定義の前に置くことで、func()呼び出しに hogehoge() 関数で定義した動作を追加できます。この仕様をデコレータと呼びます。

 例えば次の例

 

def hogehoge(f): 
    def _wrapper(*args, **kwargs):
        print(f'hogehoge._wrapper(): デコレータが呼び出されました: args={args} kwargs={kwargs}')
        print(f'hogehoge._wrapper(): 関数 f を呼び出します')
        v = f(*args, **kwargs)
        print(f'hogehoge._wrapper(): 関数 f の戻り値: {v}') 
        return v

    return _wrapper

@hogehoge 
def func(*args, **kwargs): 
    print(f"funcが呼び出されました")
    print(f" args=", *args)
    print(f" kwargs=", kwargs)
    return 1.0


func(3.0, a = 'abc')

は、

hogehoge(func)(3.0, a = 'abc')
  # f = hogehoge(func)    # <= hogehoge._wrapper()関数の参照が f に入る
  # f(3.0, a = 'abc')    # <= hogehoge.wapper(*args, **kwargs) 関数が呼び出される。
                           # *args と **kwargsは func(3.0, a = 'abc') に渡された位置引数 3.0 とキーワード引数 {"a": 'abc'} に対応

と同じ動作をします(異なる書き方で同じ動作をすることを Syntax sugar であるといいます)。
つまり、hogehoge(func)は hogehoge._wrapper() 関数を返すので、
hogehoge(func)(3.0, a = 'abc') により hogehoge._wrapper(3.0, a = 'abc') が呼び出されます。 
hogehoge._wrapper(3.0, a = 'abc') 内ではfunc(3.0, a = 'abc')を呼び出していますが、その前後に追加処理を行っています。

 デコレータは、このような共通の追加処理を関数に追加するための仕組みです。

 

 Plotly Dashの場合、@app.callback() の app は

app = Dash(__name__)

と、Dashの変数になっており、@app.callback の実体は Dashクラスで定義されている .callback() 関数です。


自作サーバ(apache)へのデプロイ

参照: https://qiita.com/NAKA_G/items/f34738df364af8cbd58e