Jupyter上でDashを使えるjupyter_dash


Dashは可視化をインタラクティブに行えるウェブフレームワークです。そしてDashにはJupyter上でアプリケーションを動作させるjupyter_dashというパッケージが存在しています。そして最近、Google Colab上でjupyter_dashが動作するようになりました。

今回はjupyter_dashの使い方に加えて、Jupyter上でDashが使えるメリットを紹介したいと思います。

今回はグーグルコラボ上で全ての作業を行います。サンプルのノートは次のリンク先にあります。

準備

jupyter_dashはコラボにインストールされていません。あと可視化に使うplotlyのバージョンが古いものになっているので、更新します。

!pip install jupyter_dash
!pip install --upgrade plotly

次に今回利用するライブラリをインポートします。

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

jupyter_dashはJupyter上でDashを使うためのパッケージです。dash_core_componentsは様々なツール、dash_html_componentsはHTMLコンポーネントを提供するパッケージです。plotly.expressはグラフ作成のパッケージです。

実践

ここからはPlotly ExpressのGapminderデータを使って進めます。Gapminderデータは1952年から2007年までの世界の国々の平均寿命、人口、1人当たりGDPを持つデータセットです。

gapminder = px.data.gapminder()
gapminder.head()

平均寿命を線グラフで可視化(1つの国)

まずは、国ごとの平均寿命を観察したいとします。Plotly Expressのグラフの書き方は、次のようになります。

  • 描きたいグラフ種類の関数を使う
  • グラフに描画したいデータフレームを渡す
  • 各要素に利用する要素を引数に渡す

まずは日本の平均寿命の推移を可視化します。

# コード1
jp_gapminder = gapminder[gapminder["country"] == "Japan"] # Japanのデータフレーム作成
px.line(jp_gapminder, x='year', y="lifeExp") # グラフ描画

次に中国の平均寿命を観察したい場合だと、たいていの場合中国のデータフレームを作成し、同じように関数の引数にデータを渡し、シフト+エンターという感じで実行すると思います。

しかし、jupyter_dashを使うとドロップダウンを選択するだけで、グラフを切り替えられ、良い感じにシフトとエンターの摩耗を防げます。

# コード2
# JupyterDashインスタンスの作成
app = JupyterDash(__name__)

# layout属性にレイアウトを渡す(ドロップダウンとグラフ)
app.layout = html.Div([
                       dcc.Dropdown(id="my_dropdown",
                                    options=[{"value": cnt, "label": cnt} for cnt in gapminder.country.unique()],
                                    value="Japan"
                                    ),
                       dcc.Graph(id="my_graph")
])

# ドロップダウンの選択値をグラフに反映するためのコールバック関数
@app.callback(Output("my_graph", "figure"), Input("my_dropdown", "value"))
def update_graph(selected_country):
  selected_gapminder = gapminder[gapminder["country"] == selected_country]
  return px.line(selected_gapminder, x="year", y="lifeExp")

# ノート上で実行
app.run_server(mode="inline")

上のようなコードでドロップダウンの選択が反映されたグラフが描画されます。

平均寿命を線グラフで作成(複数国)

複数国を描画する場合、Plotly Expressでは色(引数color)で国を分けます。

# コード3
country_list = ["China", "Korea, Rep.", "Japan"]
selected_gapminder = gapminder[gapminder["country"].isin(country_list)]
px.line(selected_gapminder, x='year', y="lifeExp", color="country")

これを国を入れ替えながら詳細に見るのは結構手間です。しかし、jupyter_dashを使うと先ほどのコードを少し変えるだけで、次のような感じで簡単に複数国の表示を切り替えられるアプリケーションが作成できます。

# コード4
app = JupyterDash(__name__)

app.layout = html.Div([
                       dcc.Dropdown(id="my_dropdown",
                                    options=[{"value": cnt, "label": cnt} for cnt in gapminder.country.unique()],
                                    value=["Japan", "China", "Korea, Rep."], # ➊
                                    multi=True # ➋
                                    ), 
                       dcc.Graph(id="my_graph")
])

@app.callback(Output("my_graph", "figure"), Input("my_dropdown", "value"))
def update_graph(selected_country):
  selected_gapminder = gapminder[gapminder["country"].isin(selected_country)] # ➌
  return px.line(selected_gapminder, x="year", y="lifeExp", color="country") # ➍

app.run_server(mode="inline")

変更点は番号を振ったところです。➊ではドロップダウンで複数国が最初から選ばれるように、リストに入れて国名を渡します。➋では引数multiにTrueを渡しドロップダウンで複数国を選択できるようにします。➌では複数国が選択されたデータフレームが作成されます。➍では引数colorに"country"を渡し、線の色が国名ごとに変更されるよう指定します。

作成したアプリケーションは次のように動作します。

ツリーマップを使って可視化

最後にツリーマップを使った可視化を作成します。ツリーマップを文字で解説する文章力がないので、まず作成してみます。

# コード5
gapminder['board'] = 'world' # "board"列を追加し、'world'という文字列を追加する
px.treemap(gapminder, path=['board', 'year', 'country'], values='pop')

今回は人口のみを可視化してみました。ツリーマップは次のように動的に数値を確認できます。ツリーマップはデータを入れ子にしてみることができるため、その順番を変えるだけでもデータとして新たな発見があります。

次に、ラジオボタンで人口、1人当たりGDPの表示を切り替えながら、ドロップダウンでツリーマップの表示順を切り替えられるツールを作成します。

# コード6
app = JupyterDash(__name__)

app.layout = html.Div([
                       html.H1(id="title"), # ラジオボタンの選択を表示する
                       dcc.RadioItems(
                           id="my_radio",
                           options=[{"label": i, "value": i} for i in ["pop", "gdpPercap"]],
                           value = "pop"
                       ),
                       dcc.Dropdown(
                           id="my_drop",
                           options=[{"label": i, "value": i} for i in ['board', 'year', 'continent', 'country']],
                           value = ['board', 'year', 'continent', 'country'],
                           multi=True
                       ),
                       dcc.Graph(id="my_graph")
])

@app.callback([Output('title', 'children'),Output('my_graph', "figure")], 
              [Input("my_radio", "value"), Input("my_drop", "value")])
def update_tree(radio_select, drop_select):
# ドロップダウンで3つ以上の要素が選択されている場合のみグラフを描画
  if len(drop_select) >= 3: 
    return radio_select, px.treemap(gapminder, path=drop_select, values=radio_select)
  else:
    return dash.no_update
app.run_server(mode="inline")

30行に満たないコードですが、次のように結構複雑な動作をしてくれます。

まとめ

以上のようにjupyter_dashを使うことにより、グラフ描画でのちょっとした面倒を削減することができます(そして多分キーボードも長持ちします)。

もうちょっとDashを詳しく知りたいと思われた方は、過去の記事をご参照ください。

もうちょっと実際のデータを使った事例を知りたいという方は、WEB+DB PRESS VOL118に記事を書かせていただいたので、手に取っていただけますと幸いです。

また、PyConJP2020で行ったTutorialの資料を公開しているので、これも参考になるかと思います。こちらはデータの前処理、可視化、機械学習との流れとなっています。スターをつけてもらえると嬉しいです。

もっと詳しく知りたいという方は、本家のドキュメントを参照していただくか、11月ごろに出る本を購入していただけますと幸いです。宣伝です。

あと、PyCon mini Hiroshima2020で話す機会をいただいたので、アイデアを練っています。イベントに参加いただき、当日のトークを聞いていただけると嬉しいです!