python のデータ可視化のフレームワークに Dash(https://dash.plotly.com/) があります。
これが Google Colab でも利用可能に、、と思ったのですが、すでに解説されている良記事を見つけてしまいました。
https://qiita.com/OgawaHideyuki/items/725f4ffd93ffb0d30b6c
ということで、この記事は参考にさせていただいて自分の手を動かしました、という記録です。
お題は安直に各国のコロナ感染者数の可視化です。地図と時系列のグラフを表示してみます。
2020-12-27 追記: 地図表示を追加しました。
使用する環境やリソースなど
Google Colaboratory を使用します。
各種リソース
-
Dash https://dash.plotly.com/
- グラフライブラリである Plotly と Flask を使用して、インンタラクティブなデータ可視化画面を簡単に作れる Python フレームワークです。
-
国・地域別感染者データ https://github.com/CSSEGISandData/COVID-19
- Johns Hopkins 大学の国・地域別感染者データを使用します。
-
Mapbox https://www.mapbox.com/
- 地図上の散布図表示で使用する地図サービスです。
今回のノートブック
マップの散布図表示は Mapbox の token と Google drive を使用するため、ノートブックは時系列とマップで 2つに分けています。
- 時系列グラフ表示
- マップ散布図表示
時系列のグラフ表示
実行準備とデータ
まず Google Colab/Jupyter ノートブックから Dash を使用するためのパッケージをインストールします。
! pip install jupyter_dash
! pip install --upgrade plotly
Dash と関連するパッケージを import します。
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
コロナの感染者データを GitHub から取得します。
データに関しては下記のページを参考にさせていただいています。
https://dodotechno.com/covd-19-visualization/
! wget https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv
データフレームにダウンロードした csv を読み込みます。
import pandas as pd
df = pd.read_csv("time_series_covid19_confirmed_global.csv")
region ごとの集計、緯度経度の削除、国ごとの列に転置し、date 列の追加を行います。
df = df.groupby(['Country/Region'], as_index=False).sum()
df.drop(["Lat","Long"], axis=1,inplace=True)
df = df.T
df.columns = df.iloc[0]
df = df[1:]
df.reset_index(inplace=True)
df.rename(columns={'index': 'date'},inplace=True)
df
結果、得られる表は以下のようになります。
ノートブックに表示してみる
まず日本の感染者数をグラフ化してみます。
横軸を日付、縦軸が感染者数としており、第一波(4月下旬)、第二波(8月上旬)、第三波(11月)らしき増加が見えます。
px.line(df, x="date", y="Japan")
つづいて任意の国をドロップダウンで選択可能にしてみます。
ノートブック上で選択できますので、試してみてください。ランタイムが停止している場合は、メニューの「ランタイム」→「すべてのセルを実行」を行ってみてください。
https://colab.research.google.com/drive/1fUP4818fSsFFFlUHlLGNoTxq8uoL2VAu#scrollTo=Kr-FsvLIpCoN&line=1&uniqifier=1
app = JupyterDash(__name__)
app.layout = html.Div([
dcc.Dropdown(id="my_dropdown",
options=[{"value": country, "label": country} for country in df.columns.unique()],
value=["Japan"],
multi=True
),
dcc.Graph(id="my_graph")
])
@app.callback(Output("my_graph", "figure"), Input("my_dropdown", "value"))
def update_graph(selected_country):
return px.line(df, x="date", y=selected_country)
app.run_server(mode="inline")
ドロップダウンから、日本とカナダを選択して表示します。カナダも増加傾向のようですね。
続いてアメリカを追加してみると、本当に日本の比ではないですね。。やはりグラフで見るとインパクトがあります。
ワクチンが有効打になって欲しいです(人ごとではありませんが)。
地図上に表示
同じデータを使用して地図上で散布図を表示してみます。
実行準備とデータ
Mapbox という地図サービスを利用します。利用にはアクセストークンが必要になります。アカウントがない場合は、下記から sign up してアスセストークンを取得してください。
https://account.mapbox.com/
最低限、ID, パスワード, メールアドレスがあれば利用できます。
今回は Google drive にトークンを格納します。ご自身で実行される場合は、文字列としてコードに埋め込んで実行することも可能です。
ここでは例として、Mapbox のトークンの内容を貼り付けたテキストファイル mapbox-token.txt
を Google drive のマイドライブ直下にアップロードします。
Google drive をマウントして、Mapbox のトークンを読み込みます。
マウント時の OAuth のトークンは実行時に表示される URL のページに飛ぶと表示されるので、コピー&ペーストなどで入力します。
from google.colab import drive
drive.mount('/content/drive')
f = open('/content/drive/My Drive/mapbox-token.txt', 'r')
MAPBOX_TOKEN = f.read()
f.close()
Jupyter dash の import は時系列のグラフと同様です。
! 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
コロナの感染者データも同様に取得します。
! wget https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv
データフレームにダウンロードした csv を読み込みます。
df_map = pd.read_csv("./time_series_covid19_confirmed_global.csv")
df_map
今回は地域ごとの集計のみ取り、緯度経度を残して転置は行わない形にします。
df_map = df_map.groupby(['Country/Region'], as_index=False).sum()
df_map
準備ができましたので、地図で表示します。
地図上で表示
日付と色指定の基準を選択可能にして表示します。
こちらを参考にしています。
https://qiita.com/banquet_kuma/items/e02ba60661cf91af37de
- color_opt, date_opt はデータフレームの列名から、緯度経度、緯度経度+国名を除いています。
-
MAPBOX_TOKEN
は先ほど Google drive にアップしたファイルから読み込んだ値です。お手元で実行する場合は、文字列でそのまま貼り付けても OK です。- ノートブックのコードで、実行される方の Google アカウントのドライブにアクセスできるはずですが、自分のアカウント以外では試せていないので、うまくいかなかったらご指摘ください。
- 見た目も色々と変更できます。オプションについて後述します。
app = JupyterDash(__name__)
color_opt = [dict(label=x, value=x) for x in df_map.columns]
del color_opt[2]
del color_opt[1]
date_opt = color_opt.copy()
del date_opt[0]
app.layout = html.Div(
[
html.Div(
[
html.P(["date:", dcc.Dropdown(id='date', options=date_opt)]),
html.P(["color:", dcc.Dropdown(id='color', options=color_opt)]),
],
style={"width": "20%", "float": "left"}
),
dcc.Graph(id="graph", style={"width": "80%", "display": "inline-block"}),
]
)
@app.callback(Output("graph", "figure"), [Input("date", "value"), Input("color", "value")])
def update_graph(date, color):
px.set_mapbox_access_token(MAPBOX_TOKEN)
if not color:
color = date
return px.scatter_mapbox(df_map,
lat="Lat",
lon="Long",
color=color,
size=date,
size_max=20,
zoom=0,
center={'lat': 35, 'lon': 135},
title="各国のコロナ感染者数",
color_continuous_scale=px.colors.diverging.BrBG,
hover_name=date)
app.run_server(mode="inline")
日付を選択すると、その時点の累積の感染者を表示します。
2020-12-01 はこんな感じです。
表示は plotly.express.scatter_mapbox
という関数で行なっています。
plotly.express.scatter_mapbox
のパラメーターの日本語の説明が見当たらなかったので参考に簡単な説明を載せておきます(よい情報源をご存知の方、コメントなどで教えてください)。
- data_frame (DataFrame or array-like or dict) – 列名でデータを渡すための DataFrame です。
- lat (str or int or Series or array-like) – 地図上の緯度です。
- lon (str or int or Series or array-like) – 地図上の経度です。
- color (str or int or Series or array-like) – 地図上のマーカーに色を割り当てるための指定です。
- text (str or int or Series or array-like) – figure 中の表示のためのテキストラベルです。
- hover_name (str or int or Series or array-like) – マウスのホバー時のツールチップ表示名です。
- hover_data (list of str or int, or Series or array-like, or dict) – マウスのホバー時のツールチップデータです。2つ要素を指定でき、1番目の要素は表示の True(デフォルト)/False またはデータ表示フォーマットを指定します。2番目の要素は表示する値です。
- custom_data (list of str or int, or Series or array-like) – ウィジットや Dash コールバックで使用する追加のデータです。ユーザーには表示されませんが figure のイベント(投げ縄ツール、註:たぶん範囲選択)などで使用します。
- size (str or int or Series or array-like) – 地図上のマークのサイズです。
- animation_frame (str or int or Series or array-like) – アニメーションフレーム(註:アニメーションフレームがよくわかっていません。。)にマークを割り当てをするために使用します。
- animation_group (str or int or Series or array-like) – アニメーションフレーム全体で不変のオブジェクトを提供し、同一の animation_group は各フレームで同じオブジェクトを記述するように扱います。
- category_orders 軸、凡例、ファセットのカテゴリ値の順序を指定します(デフォルトは {} )。python 3.6 以前では順序が保証されません。
- labels (dict with str keys and str values (default {})) – デフォルトでは列名は、軸のタイトル、凡例のエントリ、およびホバーの図で使用されます。これを上書きする列名に対応する dict です。
-
color_discrete_sequence (list of str) – 色の循環指定です。CSS として有効な色指定を行います。
-
plotly.express.colors
特にplotly.express.colors.qualitative
は便利なカラーシーケンスがあります。
-
- color_discrete_map (dict with str keys and str values (default {})) – 有効な CSS 色の指定列です。
-
color_continuous_scale (list of str) – カラーで指定された列が連続値の数値データである場合のカラースケールを指定します。
- 便利なカラースケール
plotly.express.colors
特にplotly.express.colors.sequential, plotly.express.colors.diverging and plotly.express.colors.cyclical
で使用できます。
- 便利なカラースケール
- range_color (list of two numbers) – 指定すると連続するカラースケールの指定を上書きします。
-
color_continuous_midpoint (number (default None)) – 連続カラースケールの中間の境界点を指定します。
plotly.express.colors.diverging
を使用する場合に推奨されます。 - opacity (float) – マーカーの透明度を指定します。
- size_max (int (default 20)) – マーカーの最大サイズを指定します。
- zoom (int (default 8)) – マップのズームサイズを 0〜20 で指定します。
- center (dict) – マップの中央を緯度経度(lat, lon をキーとする dict)で指定します。
-
mapbox_style (str (default 'basic', needs Mapbox API token)) – マップのスタイルを指定します。スタイルによっては、Mapbox API token が必要です。
-
'open-street-map', 'white-bg', 'carto-positron', 'carto-darkmatter', 'stamen- terrain', 'stamen-toner', 'stamen-watercolor'
は token が不要です。 -
'basic', 'streets', 'outdoors', 'light', 'dark', 'satellite', 'satellite- streets'
は token が必要です。
-
- title (str) – タイトルを指定します。
- template (str or dict or plotly.graph_objects.layout.Template instance) – テンプレート名または定義を指定します。
- width (int (default None)) – 幅を pixels で指定します。
- height (int (default None)) – 高さを pixels で指定します。
多い。。
残課題
課題点をいくつか挙げておきます。
- 日付はスライバーバー選択などが嬉しい。
- 値の桁数が違うせいか、ほとんど見えない国があるので調整が必要か(対数で出すとか?)。
- ツールチップが表示されていない。原因不明。
おわりに
ちょっとした可視化なら簡単に実装でき、Google Colab でインターネット公開も容易です。
csv の加工が一番手間かも。
地図上の可視化なども追加してみたいと思います。(→2020-12-27 地図表示を追加しました、残課題あり)
まだ色々可視化できそうなので、そのうちやるかもしれません。
実行してみてわかりましたが、Google Colab のランタイム停止時にグラフの表示が残らないので、公開が必要な場合は Heroku かノートブックなら他の方法(matplotlib, plotly など)も併用した方が良いかもしれません。
関連・参考 URL
Dash 関連
- Dash https://dash.plotly.com/
- Jupyter上でDashを使えるjupyter_dash https://qiita.com/OgawaHideyuki/items/725f4ffd93ffb0d30b6c
データ関連
- COVID-19 Data Repository by the Center for Systems Science and Engineering (CSSE) at Johns Hopkins University https://github.com/CSSEGISandData/COVID-19
- コロナ感染者数をPython+Matplotlibで可視化してみました。(4/4現在)
https://dodotechno.com/covd-19-visualization/
Mapbox 表示関連
- Mapbox https://www.mapbox.com/
- OBDから取得した車両データをDashとPlotly Expressで可視化する その2 https://qiita.com/banquet_kuma/items/e02ba60661cf91af37de
- plotly.express.scatter_mapbox https://plotly.github.io/plotly.py-docs/generated/plotly.express.scatter_mapbox.html