dash入門 データ可視化

【Plotly Dash】ドロップダウン・メニューをマスターする

さて、前回はPloly Dashを使った基本的なダッシュボードの作成方法を見てきました。

今回はDashのコントロールの中でももっともよく使うコントロールの一つであるドロップダウンについて、細かく解説したいと思います。

このような簡単なダッシュボードを作成します。

ドロップダウンに関する公式HPの解説はこちらです。

まず、準備をしておきましょう。

JupyterDashなど必要なものをインポートしておきます。

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go

dcc.Dropdown

ドロップダウン・メニューのためのリストを作成します。

表示するラベルの作成

ここでは、株価データを使うので、以下のような銘柄を指定するためのドロップダウン・メニューのリストを作成します。

drop_down_optionsという辞書のリストを作成します。

drop_down_options = [{'label': 'スノーピーク', 'value': 'スノーピーク'},
                     {'label': '楽天', 'value': '楽天'},
                     {'label': 'ANA', 'value': 'ANA'},
                     {'label': 'ぐるなび', 'value': 'ぐるなび'},
                     {'label': 'アルペン', 'value': 'アルペン'},
                     {'label': '三越', 'value': '三越'},
                     {'label': 'トヨタ', 'value': 'トヨタ'}]

メニューは辞書のリストで作成します

辞書の中身はlabelvalueを指定します。

それぞれの意味は以下です。

  • label
    表示するテキストを指定します。
  • value
    ラベルを選択した際に渡される値を指定します。

他に指定できるものには以下のようなものがあります。

  • disabled
    表示はしますが選択できないようにします。
  • title
    そのラベルにカーソルを当てた際に表示するコメントを表示します。

上記の2つについては、“株価をそのまま表示する”か、“基準化する”かを選択するドロップダウン・メニューで使ってみましょう。

以下の3つのラベルを表示します。

  • 原系列を表示する。
  • 開始時点を100として基準化する。
  • 正規化する。ただし、現状は使えない設定にします。
drop_down_normalize = [{'label': '原系列', 'value': '原系列', 
                        'title': 'もとの株価を表示します.'},
                       {'label': '開始時点を100にする', 'value': '基準化', 
                        'title': '開始時点を100とて比較しやすくします.'},
                       {'label': '正規化', 'value': '正規化', 
                        'disabled': True,
                        'title': '今は使用できません.'}]

ドロップダウン・メニューの作成

とりあえず、CSSやserverの設定をしましょう。

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
server = app.server

次に、ドロップダウンはdash_core_componentDropdownで作成します。

コードは以下のようになります。

app.layout = html.Div([html.H1('Python Dash 第二回'),
                       html.H2('ドロップダウンを使った株価チャート'),
                       html.Div([html.Div(dcc.Dropdown(id='stock_chart_dropdown_1',
                                              options=drop_down_options,
                                              multi=False,
                                              placeholder='銘柄1'
                                              ),
                                          style={'width': '15%', 'display': 'inline-block',
                                          'margin-right': 10}),
                                 html.Div(dcc.Dropdown(id='stock_chart_dropdown_2',
                                                       options=drop_down_options,
                                                       multi=False,
                                                       placeholder='銘柄2'),
                                          style={'width': '15%', 'display': 'inline-block',
                                                 'margin-right': 10}),
                                 html.Div(dcc.Dropdown(id='stock_chart_dropdown_normalize',
                                                       options=drop_down_normalize,
                                                       multi=False,
                                                       clearable=False,
                                                       value='原系列'),
                                          style={'width': '20%', 'display': 'inline-block'})
                                 ]),
                       html.Div(id='stock_chart')
                               ], style={'margin': '5%'})

ハイライト部分がドロップダウン・メニューの箇所を表しています。

全体としては、html.Divの中に各コンポーネントをリストの形で入れていきます

最初はH1タグ、H2タグとします。

そして、そのあとにdcc.Dropdownでドロップダウン・メニューを作成しますが、3つのメニューを横に並べたいので、さらにhtml.Divを加えて、その中に3つのdcc.Dropdownを入れてやります

各dcc.Dropdownをさらにhtml.Divで囲んでいますが、これはstyleを指定して、幅や余白を調整するためです。

例えば以下の箇所です。

style={'width': '15%', 'display': 'inline-block',
       'margin-right': 10}),

"display": "inline-block"としているのは、Divタグをインライン要素にして横並びにするためです。

このあたりはHTMLやCSSの知識があるといいと思いますが、ここでは説明しませんので、他のサイトや書籍などをご参照いただければと思います。

全体としては以下のような構成になります。灰色がDivタグで、青がその他のコンポーネントになります。

プロパティ

いくつかプロパティを設定しているので、そこについて説明しておきます。

id

まず、idですが、そのコンポーネントのIDを表します。

このidを使って、コールバックのインプット・アウトプットを設定します

options

optionsは上記で作成したラベルのリストを設定します。

multi

multiをFalseとすると複数の選択はできなくなり、1つのみ選択できるようになります。

multi=Trueとした場合は、複数を選択できるようになります。

実際の挙動については前回の記事をご参照ください。

placeholder

placeholderは、そのドロップダウン・メニューが何を表しているかをわかりやすくするために、灰色で表示するものです。

clearable

clearableは何も選択しない状態にできるかどうかを指定します。

Trueにすると×ボタンで消去することができます。

ここでは、原系列にするかどうかのドロップダウン・メニューは必ず選択しないといけないため、Falseに設定しています。

value

最後にvalueを設定していますが、これはデフォルトで表示するラベルを指定します。

プロパティまとめ

他にもありますので、よく使うものをこちらでまとめておきます。

プロパティまとめ
  • id
    コンポーネントのID
  • options
    ラベルを設定する
  • multi
    複数選択可能であればTrue、単一のみであればFalse
  • placeholder
    灰色でをドロップダウンの説明を表示する
  • value
    デフォルトで設定するラベル
  • clearable
    Trueにすると×ボタンで消去することができる。Falseにすると消去できない
  • disabled
    Trueにすると選択できない

では、ここでいったんどのようなダッシュボードになるのか見てみましょう。

以下を実行します。修正した場合は、必ずapp = JupyterDash(name, external_stylesheets=external_stylesheets)から再実行しましょう。

app.run_server()

このような簡単なダッシュボードが出来上がります。

グラフの作成

まだ、現時点では何も起こりません。

これにグラフを付けていきましょう。

グラフのデータ部分を作成する関数を作成しておきます。

def plot_data(fig, df, company_name, color, normalize, is_first):
  if normalize == '原系列':
    y = df[company_name]
    fig.add_annotation(text=f"<b>{company_name}",
                     x=df.iloc[0]['Date'],
                     y=y.iloc[0],
                     arrowcolor='white',
                     font=dict(color=color,
                               size=15)
                     )
  
  elif normalize == '基準化':
    y = df[company_name] / df.iloc[0][company_name] * 100
    fig.add_annotation(text=f"<b>{company_name}",
                       showarrow=False,
                       x=df.iloc[0]['Date'],
                       y=90 + is_first * 20,
                       font=dict(color=color,
                                 size=15)
                       )

  fig.add_trace(go.Scatter(x=df['Date'],
                           y=y,
                           name=company_name,
                           line=dict(width=3,
                                     color=color)
                           )
                )

詳細についてはここでは解説しませんので、必要に応じてplotlyを使った線グラフの記事をご参照ください。

コールバックの作成

コールバックは、まず@app.callbackで何をインプットとして、何をアウトプットとするかを指定します

この場合、インプットは銘柄1、銘柄2、基準化するかどうかを表す3つのドロップダウン・メニューになります。

アウトプットはDivコンポーネントに出力するグラフになります。

@app.callback(
  Output(component_id='stock_chart', component_property='children'),
  Input(component_id='stock_chart_dropdown_1', component_property='value'),
  Input(component_id='stock_chart_dropdown_2', component_property='value'),
  Input(component_id='stock_chart_dropdown_normalize', component_property='value')
)

インプットのcomponent_idは、app.layoutに設定したインプットとなるコンポーネントのIDを指定します。

ここでは、“stock_chart_dropdown_1"、"stock_chart_dropdown_2"、"stock_chart_dropdown_normalize"になります。

component_propertyはcomponent_idで指定したコンポーネントのどのプロパティを使うか?を指定します。

ここでは、ドロップダウンのvalueをインプットとするので”value"を指定しています

アウトプットは、グラフをIDが"stock_chart"のDivコンポーネントの"children"プロパティに出力するので、component_propertyは"children"を指定しています。

最後に、インプットに従って動的にグラフを作成する部分を作りましょう。

関数名は何でも買いませんが、引数はInputで指定した3つの値に対応させます

それぞれcompany_name_1, company_name_2, normalizeとします。

normalizeは"原系列"、"基準化"を取ります。"正規化"は実装していませんが、disabled=Trueとしているので選択されることはありません。

def update_graph(company_name_1, company_name_2, normalize):
  if company_name_1 is None or company_name_1 == "":
    return None
  fig = go.Figure()
  plot_data(fig, df, company_name_1, 'royalblue', normalize, is_first=True)

  if company_name_2 is not None and company_name_2 != "":
    plot_data(fig, df, company_name_2, 'lightgrey', normalize, is_first=False)

  fig.update_layout(title=f'{company_name_1}の株価推移',
                    showlegend=False,
                    plot_bgcolor='white',
                    width=1000,
                    height=500,
                    )
  fig.update_xaxes(showline=True,
                   linewidth=1,
                   linecolor='lightgrey',
                   color='grey',
                   ticks='inside',
                   ticklen=5,
                   tickwidth=2,
                   tickcolor='lightgrey'
                   )
  fig.update_yaxes(title=dict(text='stock price',
                              font_color='grey'),
                   showline=True,
                   linewidth=1,
                   linecolor='lightgrey',
                   color='grey'
                   )
  return dcc.Graph(figure=fig)

あとは引数に従って、グラフを作成しているだけです。

グラフの細かい設定の仕方については、以下の記事等をご参照ください。

DashやCallbackと直接関係するわけではありませんので、ここでは細かいところは無視していただいても大丈夫です。

最後にreturn dcc.Graph(figure=fig)で、dcc.Graphを返しています。

その際にfigureプロパティに作成したグラフfigを設定しています。

これで、Callbackが呼ばれると、Divタグにdcc.Graphが設定されます。

では、どのようになるか見てみましょう。

今までと同様に以下を実行します。

app.run_server()

このようなダッシュボードができます。

2つの株価を基準化したりして比較することができます。

plotlyがインタラクティブなグラフを作成するものなので、カーソルを当てて数値を確認したり、気になるところを拡大したりすることができます。

また、ドロップダウンは検索ができるので候補が多い場合に非常に便利です。

まとめ

今回はドロップダウン・メニューについて詳しく見てきました。

初めはエラーが出て困ることもあるかもしれませんし、複雑に感じるかもしれませんが、すぐに慣れると思いますので、楽しみながら色々試してみてください。

最後の方のグラフの設定については、ドロップダウンなどとは直接関係する部分ではありませんが、データ分析をする人、分析結果を人に見せる人にとっては非常に重要な技術です。

以下の記事では、データ可視化技術や関連本を紹介していますので、参考にしていただければと思います。

【データ可視化】作ってはいけないグラフ6つ

データ・ビジュアライゼーションのオススメ本

次はその他のコンポーネントについて紹介したいと思います。

では!

-dash入門, データ可視化
-, ,