Python
Dash
pandas
可視化

Dashでプレゼン資料を作った!


口上

これまで4つのDashの記事を作りました。

Dashの記事を書いたモチベーションは、データを使ったプレゼンテーションはつまらないのは、データ量が極小になっていて、発表者の示した経路しか見えず「まあそうですね」で終わることが原因で、


それを終わらすのがDashです!!

ということがいいたかったからです。

といいながら、そのプレゼンテーションを示してきませんでした。

というわけで、今回はプレゼン資料を作ってみたよってお話です。


プレゼン資料内容

今回作った資料は、Pydata Osaka可視化特会(3/24)と大阪Pythonの会(4/5)で使ったものをちょっとバージョンアップしたものです。

プレゼン資料with Dash(リンク先にDashで作ったプレゼン資料があります)

この資料の内容は、データを使ったプレゼンのつまらない理由を話したあとに、Dashだとデータをたくさん詰め込んだ可視化ができるよ!って話をして、その後使い方を解説しています。

使い方はDATA11のページからで、14で終わるので、短く見えるけど、それまでのプレゼン風から、普通のサイト風に作っているからであって、使い方はかなり長いです。

Qiitaは技術の話以外書いたら怒られると聞いたので、とりあえず、技術の話を書いていきましょう。


注意!

Dashはウェブアプリケーションにした時に最も破壊力があるのですが、dashの最新バージョン0.40.0でherokuに上げると、エラーが出て、ちゃんと動かなかったです。reactっぽいエラーが出てきて、理由はよく分かりません。


基本形よくあるグラフ

以降ではプレゼンの概要を示していきます。

詳細はプレゼン資料を見て頂けると幸いです


最初にお断り

Dashはウェブアプリケーションなので、html要素もちょっと書かなければいけません。よく、「html,cssなしでウェブアプリが描ける最高や!」みたいに書かれていますが、ちょっと分かっていないと書けないことは最初に断っておきます。


プレゼンテーションのDATA11の最初のグラフ

Image from Gyazo

よく見る簡単なグラフ。しかしこれだけでも、簡単にインタラクティブなチャートになっていることがわかります。

普通、レジェンドを出すのもちょっとコードを書かなければいかなかったりするのですが、Dashの場合、必要ありません。また、そのレジェンドをクリックすると要素が消せたり出せたりします。良い。コードは以下のような感じです。

import dash

import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(children=[
html.H1(children='Hello Dash'),

html.Div([
html.P('Dash: A web application framework for Python.')
]),

dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}})
])

if __name__ == '__main__':
app.run_server(debug=True)

長いように思いますが、グラフのために書くのは5,6行です。これだけで、レジェンドクリックでインタラクティブに動くのはすごいですよね〜。


Dashで最も重要な機能!Callback

上のようなものだと、まぁ他でも頑張ればできるよねぇみたいになります。

しかし、Callbackを使えば、他ではありえない動きが得られます。

その例をDATA12の真ん中のグラフで見てみましょう。

Image from Gyazo

ここではレディオアイテムで表示するデータを変えるということと、マウスのホバーで得られるデータをグラフの上に表示させています。

その2つをCallbackで返しています。コードは以下のようになっています。

import dash  

import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
import json

df = pd.read_csv('./data/longform.csv', index_col=0)
dfhokkaido = df[df['area']=='北海道']

app = dash.Dash(__name__)

app.layout = html.Div(children=[
html.Div(
html.H1('北海道のGDP、人口、一人あたりGDPの推移',
style = {'textAlign': 'center'})
),
html.Div(
html.H1(id='add-hover-data')
),
dcc.RadioItems(
id = 'dropdown-for-hokkaido',
options = [{'label': i, 'value': i} for i in dfhokkaido.item.unique()],
value = 'GDP'
),
dcc.Graph(
id="hokkaidoGraph",
)
])

@app.callback(
dash.dependencies.Output('hokkaidoGraph', 'figure'),
[dash.dependencies.Input('dropdown-for-hokkaido', 'value')]
)
def update_graph(factor):
dff = dfhokkaido[dfhokkaido['item'] == factor]

return {
'data': [go.Scatter(
x = dff['year'],
y = dff['value']
)]
}

@app.callback(
dash.dependencies.Output('add-hover-data', 'children'),
[dash.dependencies.Input('hokkaidoGraph', 'hoverData')]
)
def return_hoverdata(hoverData):
return json.dumps(hoverData)

if __name__ == '__main__':
app.run_server(debug=True)

この機能が使えるかに、Dashすげーってなるかどうかがかかっています。がんばりましょう!


リアルタイムデータのアップデート

DATA13のはじめのチャートです。

リアルタイムなアップデートもdccのInterval機能を使えば可能です。

機能と呼ぶにはちょっと簡単すぎますが。何しろ更新時間だけ指定すればよいのです。

この資料ではちょっとエセリアルタイムな動かし方をしています。過去の為替レートと今の時間をシンクロさせて、リアルタイムっぽく動かしています。そのため日付は昔の日付です。

Image from Gyazo

import dash  

import dash_core_components as dcc
import dash_html_components as html
from datetime import datetime
from datetime import timedelta
import pandas as pd
import plotly.graph_objs as go

df = pd.read_csv('https://raw.githubusercontent.com/plotly/dash-web-trader/master/pairs/USDJPY.csv',
index_col=1, parse_dates=['Date'])
dff = df['2016/1/5']
dff = dff.resample('1S').last().bfill()

app = dash.Dash(__name__)

app.layout = html.Div([
html.Div([
dcc.Graph(id="usdjpy"),
dcc.Interval(
id = 'interval_components',
interval = 1000,
)
], style={'height': '30%', 'width': '80%', 'margin': '0 auto 0', 'textAlign': 'center'})
])

@app.callback(
dash.dependencies.Output('usdjpy', 'figure'),
[dash.dependencies.Input('interval_components', 'n_intervals')]
)
def update_graph(n):
t = datetime.now()
nowHour = t.hour
nowMinute = t.minute
nowSecond = t.second

d = datetime(2016, 1, 5, nowHour+9, nowMinute, nowSecond)
period = timedelta(seconds = 120)
d1 = d - period
dff1 = dff.loc['{}'.format(d1): '{}'.format(d), :]

return {
'data': [go.Scatter(
x = dff1.index,
y = dff1['Bid']
)],
'layout':{
'height': 600,
'title': 'USD-JPY 1Second Charts'
}
}

if __name__=='__main__':
app.run_server(debug=True)

dのところで時間に9を足しているのはまぁちょっとしたやり方でということで、ここではあまり触れません。ちなみにMacで動かす場合は+9を取り除かないと動きません。あしからず。


グラフも描ける

2月に新機能として発表されたグラフ。

これを使っていろいろかっこよく、関係を示したいところ。私はブロックチェーンもやっているので。しかしRDF形式をきれいに読み込ますことが出来ず、今のところはあって、こんな風に使えるということを示すだけになってしまいます。すみません。

現時点では有向にできませんと書いていますが、私が見た限りで、もしかしたらできるかもなので、もし出来た場合、お知らせ頂けると嬉しいです!

Dash Cytoscape解説ページ

Image from Gyazo

import dash

import dash_cytoscape as cyto
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

nodes = [
{
'data': {'id': short, 'label': label},
'position': {'x': 20*lat, 'y': -20*long}
}
for short, label, long, lat in (
('la', 'Los Angeles', 34.03, -118.25),
('nyc', 'New York', 40.71, -74),
('to', 'Toronto', 43.65, -79.38),
('mtl', 'Montreal', 45.50, -73.57),
('van', 'Vancouver', 49.28, -123.12),
('chi', 'Chicago', 41.88, -87.63),
('bos', 'Boston', 42.36, -71.06),
('hou', 'Houston', 29.76, -95.37)
)
]

edges = [
{'data': {'source': source, 'target': target}}
for source, target in (
('van', 'la'),
('la', 'chi'),
('hou', 'chi'),
('to', 'mtl'),
('mtl', 'bos'),
('nyc', 'bos'),
('to', 'hou'),
('to', 'nyc'),
('la', 'nyc'),
('nyc', 'bos')
)
]

elements = nodes + edges

app.layout = html.Div([
dcc.Dropdown(
id='dropdown-update-layout',
value='grid',
clearable=False,
options=[
{'label': name.capitalize(), 'value': name}
for name in ['grid', 'random', 'circle', 'cose', 'concentric']
]
),
cyto.Cytoscape(
id='cytoscape-update-layout',
layout={'name': 'grid'},
style={'width': '100%', 'height': '450px'},
elements=elements
)
])

@app.callback(Output('cytoscape-update-layout', 'layout'),
[Input('dropdown-update-layout', 'value')])
def update_layout(layout):
return {
'name': layout,
'animate': True
}

if __name__ == '__main__':
app.run_server(debug=True)


githubとherokuをつなぐ!(1行もコード書かない)

ウェブで共有するのにはherokuが便利です。

以前書いた記事では7行で上げるという短いのか長いのかわからない行数を示しましたが、今回は1行もコードを書かずに、githubにあげたコードがheroku上で動くやり方を提示します。

まずはherokuにアカウントを作ります。そしてダッシュボードからアプリ名を登録します。下のように使える名前だと使えるよーっていうことで、作成できます。

Image from Gyazo

次にデプロイ画面になるので、ここでメソッドでgithubを選択すると、リポジトリを探す画面が出てくるので、そこにアップしたいリポジトリの名前を入れます。

Image from Gyazo

すると、これかって聞いてくるので、あっていればconnectを押します。

そして、下に行きmanual deployを行います。

Image from Gyazo

うまくいくとDeploy to Herokuとの文字が出てくるので、その下のviewをクリックしましょう。ちゃんと出来ていれば、あなたの作ったアプリが動き始めるはずです!

Image from Gyazo


まとめ

以上のような感じで、チャートが描け、ウェブ上でアップできます。

これらのような機能を使って、データをたくさん詰め込んだ資料で、良い発見を行い、日本の社会がよりよく発展していくと良いなと思っています。

そのためのデータの可視化にDashは良いぞということを書いて、この記事を終えたいと思います。