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で話す機会をいただいたので、アイデアを練っています。イベントに参加いただき、当日のトークを聞いていただけると嬉しいです!