はじめに
アポロ株式会社でインターンをしている菅野と申します。
今回は比較的簡単にWebアプリ開発ができるPythonフレームワークの1つであるDashとグラフ描画ツールであるPlotlyを使ったダッシュボード作成を紹介します。
私は、現在インターン生としてSNS上での情報拡散がどのように売り上げに影響を及ぼしているのかを分析しています。この分析では、得られるデータが非常に多く、動的であるため、プレゼンをする際のパワーポイントによる可視化はとても厄介です。動的なデータの分析結果をどのようにすれば効果的に見せることができるかまで考え、実際に動かしながらプレゼンできるダッシュボードを作成しました。また、顧客にもダッシュボードを触ってもらって分析してもらえるようにしました。
Dashとは
DashとはPlotly社が開発したFlaskをベースにしたPythonフレームワークで、htmlやjavascriptを使わずに比較的簡単にWebアプリ開発をすることができます。グラフ描画ライブラリのplotlyと連携することで、ダッシュボードを作成し素早くデータを可視化することができるのが特徴です。
データを可視化することで、プレゼンでの説明をわかりやすく説得力のあるものにすることができます。ダッシュボードをクライアントに渡せば、クライアント自身がコードを書かずにデータ分析できるようになります。また、後に説明するコールバックを実装すれば、インタラクティブにデータを操作し、隠れたインサイトを発掘することができます。今回実施した分析では、商品の売り上げがSNSの情報拡散にどれくらい遅れて上昇するのかやSNSの種類による売り上げへの影響の与え方の違いなどが分かりました。
実装例
本稿では、Plotlyに用意されてある株価のデータセットを用い、株価推移を表示するダッシュボードをGoogle Colaboratory(Jupyter Notebookでも可)で作ってみます。
まず、必要なライブラリをインストールします。
今回使用したライブラリのバージョンは、dashが2.8.1、plotlyが5.5.0、jupyter_dashが0.4.2です。
!pip install dash jupyter_dash
次に、必要なライブラリをインポートします。
#必要なライブラリのインポート
from jupyter_dash import JupyterDash
from dash import html, dcc, Input, Output
import pandas as pd
import plotly
import plotly.graph_objects as go
表示させるデータをダウンロード、加工します。
今回はplotlyのデータライブラリにある株価データを使用します。
2018年から2019年までのGoogle、Apple、Amazon、Facebook、Netflix、Microsoftの相対株価(2018年1月1日を1とした値)を週次で格納しています。データの中身は以下のようになっています。
これを読み込みます。
#株価データを読み込む
df = plotly.data.stocks()
これで下準備が完了しました。これからダッシュボードを作成します。
#appの定義
app = JupyterDash(__name__)
#plotlyでグラフを作成
fig = go.Figure()
fig.add_trace(go.Scatter(x=df['date'], y=df['GOOG'], name='Google'))
#レイアウトを設定し、その中で作成したグラフを描画
app.layout = html.Div(
[
dcc.Graph(figure=fig)
]
)
#appを実行
app.run_server()
まず、appを定義します。plotlyでグラフを作成し、レイアウトを設定すると、静的なダッシュボードが完成します。このコードを実行すると、下にURLが出力されます。このURLをクリックすると、別のタブで、以下の株価に関する時系列グラフが表示されます。(表示に時間がかかる場合があります。)
plotlyで作成されたグラフはマウス操作で自由に拡大や表示範囲の変更、表示されたグラフの保存をすることができます。
次にコールバックを実装します。コールバックとは入力する値によって、出力を変更することができる機能で、例えば、ユーザーが選んだ項目によってグラフの表示を変更することができます。
今回はドロップダウンリストを使って株価を表示する企業、ラジオボタンを使って時系列粒度を変更できるようにします。ドロップダウンリストとはたくさんの選択肢の中からいくつか選ぶタイプのメニューバーで選択肢が増えても場所をとらずに表示できます。ラジオボタンは複数の選択肢から一つだけ選ぶタイプのメニューバーです。
ドロップダウンリストとラジオボタンを実装するため、上のコードのレイアウトの設定部分を以下のように変更します。
#レイアウトの設定を変更
app.layout = html.Div(
[
#ドロップダウンの実装
dcc.Dropdown(
id='dropdown', #コールバックで使用する。好きな名前をつける。
value=['GOOG'], #初期値を設定
multi=True, #複数選択可なら「True」、不可なら「False」
options=[{'label':x,'value':x} for x in ['GOOG','AAPL','AMZN','FB','NFLX','MSFT']] #選択肢を設定
),
dcc.RadioItems(
id='radioitems', #コールバックで使用する。好きな名前をつける。
value='w', #初期値を設定
options=[{'label':'週次','value':'w'},{'label':'月次','value':'m'},{'label':'年次','value':'y'}] #選択肢を設定
),
html.Div(
id='graph' #コールバックで使用する。好きな名前をつける。
)
]
)
肝心のコールバックを実装します。コールバックは@app.callbackで入力元のidとプロパティを受け取り、その下の関数の結果を出力先のidの指定したプロパティに出力します。
#コールバックの実装
@app.callback(Output(component_id='graph',component_property='children'), #Outputのcomponent_idで指定した出力先のcomponent_propertyに下の関数の結果が代入される
Input(component_id='dropdown',component_property='value'),
Input(component_id='radioitems',component_property='value'))
def draw_grpah(company,freq): #Inputの順番通りに引数を指定
df = plotly.data.stocks()
#時系列粒度を変更できるようにindexをdatetime型に変更する
df = df.set_index(pd.to_datetime(df['date']))
#時系列粒度を入力された値に変更する
df = df.resample(freq).mean()
df = df.reset_index()
#plotlyでグラフを作成する
fig = go.Figure()
for c in company:
fig.add_trace(go.Scatter(x=df['date'], y=df[c], name=c))
return dcc.Graph(figure=fig)
ここまでのコードは以下です。
#必要なライブラリのインポート
from jupyter_dash import JupyterDash
from dash import html, dcc, Input, Output
import pandas as pd
import plotly
import plotly.graph_objects as go
#appの定義
app = JupyterDash(__name__)
#レイアウトの設定を変更
app.layout = html.Div(
[
#ドロップダウンの実装
dcc.Dropdown(
id='dropdown', #コールバックで使用する。好きな名前をつける。
value=['GOOG'], #初期値を設定
multi=True, #複数選択可なら「True」、不可なら「False」
options=[{'label':x,'value':x} for x in ['GOOG','AAPL','AMZN','FB','NFLX','MSFT']] #選択肢を設定
),
dcc.RadioItems(
id='radioitems', #コールバックで使用する。好きな名前をつける。
value='w', #初期値を設定
options=[{'label':'週次','value':'w'},{'label':'月次','value':'m'},{'label':'年次','value':'y'}] #選択肢を設定
),
html.Div(
id='graph' #コールバックで使用する。好きな名前をつける。
)
]
)
#コールバックの実装
@app.callback(Output(component_id='graph',component_property='children'), #Outputのcomponent_idで指定した出力先のcomponent_propertyに下の関数の結果が代入される
Input(component_id='dropdown',component_property='value'),
Input(component_id='radioitems',component_property='value'))
def draw_grpah(company,freq): #Inputの順番通りに引数を指定
df = plotly.data.stocks()
#時系列粒度を変更できるようにindexをdatetime型に変更する
df = df.set_index(pd.to_datetime(df['date']))
#時系列粒度を入力された値に変更する
df = df.resample(freq).mean()
df = df.reset_index()
#plotlyでグラフを作成する
fig = go.Figure()
for c in company:
fig.add_trace(go.Scatter(x=df['date'], y=df[c], name=c))
return dcc.Graph(figure=fig)
#appを実行
app.run_server()
このコードを実行しURLを開くと、下の画像のように、選択に応じて表示が変わるようになります。
また、レイアウト設定部分においてスタイルを変更することで、ダッシュボードの見た目を変更することもできます。例えば、以下のようにレイアウトを変更すれば、ドロップダウンアイテムとラジオボタンをサイドバーとして左端に寄せることもできます。
#レイアウトの設定変更
app.layout = html.Div(
[
#サイドバーに入れたいものをDivにまとめる
html.Div(
[
dcc.Dropdown(
id='dropdown',
value=['GOOG'],
multi=True,
options=[{'label':x,'value':x} for x in ['GOOG','AAPL','AMZN','FB','NFLX','MSFT']]
),
dcc.RadioItems(
id='radioitems',
value='w',
options=[{'label':'週次','value':'w'},{'label':'月次','value':'m'},{'label':'年次','value':'y'}]
)
],style={'width':'30%','background-color':'whitesmoke'} #サイドバーの幅と色を設定
),
html.Div(id='graph',style={'width':'70%','margin-left':'auto'}) #グラフの幅を設定 'margin-left':'auto'とすることで左揃えにする
],style={'display':'flex','height':'100vh'} #'display':'flex'とすることで2つの要素を横並びにする 高さを設定
)
以下が今回のコードの全文です。
#必要なライブラリのインポート
from jupyter_dash import JupyterDash
from dash import html, dcc, Input, Output
import pandas as pd
import plotly
import plotly.graph_objects as go
#appの定義
app = JupyterDash(__name__)
#レイアウトの設定変更
app.layout = html.Div(
[
#サイドバーに入れたいものをDivにまとめる
html.Div(
[
dcc.Dropdown(
id='dropdown',
value=['GOOG'],
multi=True,
options=[{'label':x,'value':x} for x in ['GOOG','AAPL','AMZN','FB','NFLX','MSFT']]
),
dcc.RadioItems(
id='radioitems',
value='w',
options=[{'label':'週次','value':'w'},{'label':'月次','value':'m'},{'label':'年次','value':'y'}]
)
],style={'width':'30%','background-color':'whitesmoke'} #サイドバーの幅と色を設定
),
html.Div(id='graph',style={'width':'70%','margin-left':'auto'}) #グラフの幅を設定 'margin-left':'auto'とすることで左揃えにする
],style={'display':'flex','height':'100vh'} #'display':'flex'とすることで2つの要素を横並びにする 高さを設定
)
#コールバックの実装
@app.callback(Output(component_id='graph',component_property='children'), #Outputのcomponent_idで指定した出力先のcomponent_propertyに下の関数の結果が代入される
Input(component_id='dropdown',component_property='value'),
Input(component_id='radioitems',component_property='value'))
def draw_grpah(company,freq): #Inputの順番通りに引数を指定
df = plotly.data.stocks()
#時系列粒度を変更できるようにindexをdatetime型に変更する
df = df.set_index(pd.to_datetime(df['date']))
#時系列粒度を入力された値に変更する
df = df.resample(freq).mean()
df = df.reset_index()
#plotlyでグラフを作成する
fig = go.Figure()
for c in company:
fig.add_trace(go.Scatter(x=df['date'], y=df[c], name=c))
return dcc.Graph(figure=fig)
#appを実行
app.run_server()
今回の処理をフローチャートにすると下の図のようになります。
まとめ
今回はdashとplotlyを使ったインタラクティブなダッシュボードを作成する方法を紹介しました。ドロップダウンとラジオアイテム以外にも様々な機能を持ったコンポーネントが用意されているため、それらを駆使すれば大体のものは思い通りに作ることができます。
ダッシュボードを作成すると、データを直感的に捉えられるとともに、自由自在に有効な手段でデータを表示できます。みなさんもぜひダッシュボードを使ってみてください。
最後まで読んでいただき、ありがとうございます。
アポロならではの技術的課題に対する取り組みやプロダクト開発の試行錯誤で得た学びなどを定期的に発信していきます。少しでも業界へ貢献できれば嬉しいです。
今後ともよろしくお願いいたします。