この記事はデータ可視化Advent Calendar 2020の10日目です。筆者のPyData FukuokaでのLTの内容を元にしています。
TL;DR
サンキーダイアグラムは、量を伴う、時間・空間・状態の遷移の表現に最適です。PythonではPlotlyとHoloViewsに実装されています。この記事では、HoloViewsを用いた例を示します。
サンキーダイアグラムとは
サンキーダイアグラムはフローチャートに似た有向グラフで(下図1)、ネットワーク図の一種と見なすことができます。以下の性質があり、量を伴う、時間・空間・状態の遷移の表現に最適2です。「サンキー」はこのような形式のグラフをほぼ最初に用いた人の名前です。
- ノード間の関係性と順序 = 流れの⽅向
- ⽮印(線)の幅 = 流量
Pythonでの実装
筆者の知る限り、Pythonのデータ可視化パッケージでサンキーダイアグラムをサポートしているものとしては、HoloViewsとPlotlyの2つがあります。いずれも、矢印ではなく線でノードをつなぐので、一見すると無向グラフのようですが、左から右へという向きがあるようです。
- HoloViews
holoviews.Sankey
- バックエンド:BokehとMatplotlib
- Plotly
plotly.graph_objects.Sankey
Plotlyにはいい本があるので、ここではHoloViewsを使用してサンキーダイアグラムを描いてみます。
準備
インストール
conda
やpip
でインストールできます。ご自身の環境に合わせて選んで下さい。公式サイトによるとconda
の場合は以下のコマンドが良いようです。
conda install -c pyviz holoviews bokeh
pip install holoviews
import
ここではホバーなど動的なグラフを作成するためにbokehを使います。matplotlibを用いて静的なグラフを描画することもできます。
from bokeh.models import HoverTool
import holoviews as hv
from holoviews import opts, dim
hv.extension('bokeh')
データ
今回サンキーダイアグラムの作成に使用するのは、UN Comtradeという国連の貿易統計部門がまとめているデータで、2018年のスパークリングワインの輸出入に関するものです(参考:American Association of Wine EconomistsのTwitter投稿)。APIでも入手可能ですが、その説明は本題ではないので、以下のようにして用意します。
# 上位30ペアのみ(単位:キロトン)
sparklingwine = [
["Europe", "France", "North America", "USA", 736],
["Europe", "France", "Europe", "UK", 520],
["Europe", "Italy", "Europe", "UK", 513],
["Europe", "Italy", "North America", "USA", 394],
["Europe", "France", "Asia", "Singapore", 337],
["Europe", "France", "Europe", "Germany", 317],
["Asia", "Singapore", "Asia", "Japan", 225],
["Europe", "France", "Asia", "Japan", 207],
["Europe", "France", "Europe", "Belgium", 193],
["Europe", "France", "Europe", "Italy", 190],
["Europe", "France", "Europe", "Switzerland", 132],
["Europe", "Italy", "Europe", "Germany", 121],
["Europe", "France", "Europe", "Spain", 103],
["Europe", "France", "Oceania", "Australia", 103],
["Europe", "Spain", "North America", "USA", 90],
["Europe", "Spain", "Europe", "Germany", 85],
["Europe", "France", "Europe", "Sweden", 81],
["Europe", "France", "North America", "Canada", 78],
["Asia", "Singapore", "Oceania", "Australia", 72],
["Europe", "France", "Europe", "Netherlands", 71],
["Europe", "Italy", "Europe", "Switzerland", 71],
["Europe", "Spain", "Europe", "Belgium", 70],
["Europe", "Spain", "Europe", "UK", 65],
["Europe", "Italy", "Europe", "France", 63],
["Europe", "France", "Middle East", "UAE", 59],
["Europe", "Italy", "Europe", "Russia", 56],
["Europe", "Italy", "Europe", "Belgium", 54],
["Europe", "Italy", "Europe", "Sweden", 50],
["Europe", "Spain", "Europe", "France", 44],
["Europe", "Italy", "North America", "Canada", 43],
["Europe", "Italy", "Asia", "Japan", 40],
["Europe", "Spain", "Asia", "Japan", 39],
["Europe", "France", "Asia", "Hong Kong", 39],
["Europe", "France", "Europe", "Denmark", 38],
["Europe", "France", "Europe", "Austria", 37],
["Europe", "Netherlands", "Asia", "Japan", 34],
]
sparklingwine_df = pd.DataFrame(sparklingwine).rename(
{0: "region_exp", 1: "country_exp", 2: "region_imp", 3: "country_imp", 4: "value"},
axis="columns",
)
HoloViewsのサンキーダイアグラムは循環参照ができないので、輸出国と輸入国を区別するために輸出国の名前の後ろにExp
を付与します(シンガポールとオランダを除いている訳は完成したグラフを見ると分かります)。
def set_exp(df):
if df.country_exp in ["Singapore", "Netherlands"]:
return df.country_exp
else:
return df.country_exp + " Exp"
sparklingwine_df = sparklingwine_df.assign(
country_exp=lambda x: x.apply(set_exp, axis=1)
)
描画
ホバー(bokehの機能)
サンキーダイアグラムの「線」の部分にマウスを当てた時にグラフの上に表示される内容を指定します。ここでは、輸出国、輸入国、量を表示させます。名前と値のペアをタプルにし、値を@カラム名
と指定するとDataFrameのカラムから値を取得できます。
hover = HoverTool(
tooltips=[
("From", "@country_exp"),
("To", "@country_imp"),
("Value [kt]", "@value"),
]
)
グラフ本体
hv.Sankey
クラスにデータセットsparklingwine_df
、始点ノードと終点ノードのカラム名kdims
、流量(線の太さ)vdims
を指定します。label
はグラフのタイトルです。
更に、opts
メソッドを使って、サイズ(width
とheight
)や色(cmap
とedge_color
)などを指定します。ここでは輸出国ごとに線を塗り分けるため、edge_color=dim("country_exp").str()
としています。
sparklingwine_sankey = hv.Sankey(
sparklingwine_df,
kdims=["country_exp", "country_imp"],
vdims=["value"],
label="World's Top Sparkling Wine Trade Routes in 2018 (Value in million USD)",
).opts(
width=900,
height=800,
cmap="glasbey_hv",
edge_line_alpha=0,
edge_color=dim("country_exp").str(),
tools=[hover],
node_sort=True,
) * hv.Text(
500,
-15,
"Based on AAWE's post on Facebook https://www.facebook.com/wineecon/posts/3557583497601764/",
fontsize=10,
)
グラフオブジェクトsparklingwine_sankey
を呼び出すと、グラフが表示されます。シンガポールとオランダは貿易の中継地点であり、日本の輸入量においてシンガポール経由が大きな割合を占めていることが分かります。
出力
hv.save
関数を用いて、htmlやpngとして出力することができます。
hv.save(sparklingwine_sankey, 'sparklingwine_sankey.html')
hv.save(sparklingwine_sankey, 'sparklinkwine_sankey.png', dpi=600)
まとめ
サンキーダイアグラムは、量を伴う、時間・空間・状態の遷移の表現に最適です。PythonではPlotlyとHoloViewsに実装されていて、この記事ではHoloViewsを用いた例を示しました。是非使ってみて下さい!
-
例えば、ECサイト訪問者が購入に至るまでの過程を人数を含めて示す場合などです。 ↩