本サイトで2018年一番PVが多かったのがサンキーダイアグラムの可視化についての記事でした。一方で当該記事はあくまで可視化の手法について紹介したものであったのでダイアグラムそのものの作り方については触れていません。したがって本記事では「サンキーダイアグラム」の基本からPythonでの壁画方法までをレクチャーします。
サンキーダイアグラムとは
サンキーダイアグラムはinとoutをもつフローを可視化するチャートです。その流入量が多ければその辺は太くなりその逆の場合細く表現されます。古くは19世紀末に蒸気機関のエネルギー効率を論じた論文に用いられその著者の名前がそのままチャートの名前として定着したそうです。
活用の幅は広く、前述したエネルギーフローやお金の流れなどに用いられた事例があります。現在はwebマーケティングの文脈で使われる事が多いです。例えばgoogle analytycsは標準の機能としてHP上のコンテンツ間のユーザ遷移状況を可視化できます。
(筆者管理ページのGoogle Analyticsによるサンキーダイアグラム)
Pythonでの準備
以下の前提でお話しします。
- anacondaインストール済み
- Python3系
上記の設定に関してはGoogleで検索して準備してください。
https://github.com/ricklupton/ipysankeywidgetkk
このGithubを参考に進めて行きます。まずコンソール上で以下のように打ちます。
$ pip install ipysankeywidget
$ jupyter nbextension enable --py --sys-prefix ipysankeywidget
1行目はPython用のライブラリをインストールしており、2行目はJupyter Notebook上で表示できるようにしています。
実践
時系列を考慮しないタイプ
一番簡単な例を考えましょう。例えばあるものの内訳を可視化する場合を考えます。例として東京証券取引所の上場区分(1部/2部/マザーズ)ごとの製造業/非製造業の内訳を可視化します。
東証1部 | 東証2部 | マザーズ | |
総数 |
1,915 |
511 | 239 |
製造業 | 905 | 269 | 29 |
非製造業 | 1,010 | 242 | 210 |
(https://www.jpx.co.jp/markets/statistics-equities/misc/04.html 規模別・業種別PER・PBR(連結・単体)一覧 より 2018年1月を元に作成)
これを元に以下のようにPythonコードを書きます。
from ipysankeywidget import SankeyWidget
from ipywidgets import Layout
#links変数作成
#辞書を要素とするリストとして渡す
links = [
{'source': '全社', 'target': '市場1部', 'value': 1915},
{'source': '全社', 'target': '市場2部', 'value': 511},
{'source': '全社', 'target': 'マザーズ', 'value': 239},
{'source': '市場1部', 'target': '製造業', 'value': 905},
{'source': '市場1部', 'target': '非製造業', 'value': 1010},
{'source': '市場2部', 'target': '製造業', 'value': 269},
{'source': '市場2部', 'target': '非製造業', 'value': 242},
{'source': 'マザーズ', 'target': '製造業', 'value': 29},
{'source': 'マザーズ', 'target': '非製造業', 'value': 210},]
#オブジェクト化
w = SankeyWidget(links=links, margins=dict(top=0, bottom=0, left=50, right=100))
#表示
w
SankeyWidgetでの必須引数であるlinksを最初に定義します。sourceがinでtargetがoutです。linksは辞書を要素とするリストです。上記を実行すると以下のダイアグラムが得られます。
この様に時系列のないタイプは何かの内訳を可視化することに向いています。
時系列のあるもの
次に時系列の推移を可視化することを考えてみます。ある会員制プログラムの顧客ランク(S/A/B)の年次推移の架空データを以下の様に考えます。
縦軸(20×1年末)/ 横軸(20×2年末) |
S | A | B | 退会 |
S | 100 | 50 | 20 |
10 |
A | 80 | 200 | 80 | 20 |
B | 10 | 40 | 100 | 30 |
新規 | 10 | 20 | 30 | 10 |
先ほどの例と同じ様にlinksを定義します。お気付きの方もおられると思うのですがこのPythonのライブラリは異なる問題を可視化する時、引数であるlinksを工夫して対応します。
#辞書を要素とするリストとして渡す
links = [
{'source': '20x1_All', 'target': 'S_20x1', 'value': 180},
{'source': '20x1_All', 'target': 'A_20x1', 'value': 380},
{'source': '20x1_All', 'target': 'B_20x1', 'value': 180},
{'source': 'S_20x1', 'target': 'S_20x2', 'value': 100},
{'source': 'A_20x1', 'target': 'S_20x2', 'value': 80},
{'source': 'B_20x1', 'target': 'S_20x2', 'value': 10},
{'source': '新規', 'target': 'S_20x2', 'value': 10},
{'source': 'S_20x1', 'target': 'A_20x2', 'value': 50},
{'source': 'A_20x1', 'target': 'A_20x2', 'value': 200},
{'source': 'B_20x1', 'target': 'A_20x2', 'value': 40},
{'source': '新規', 'target': 'A_20x2', 'value': 20},
{'source': 'S_20x1', 'target': 'B_20x2', 'value': 20},
{'source': 'A_20x1', 'target': 'B_20x2', 'value': 80},
{'source': 'B_20x1', 'target': 'B_20x2', 'value': 100},
{'source': '新規', 'target': 'B_20x2', 'value': 30},
{'source': 'S_20x1', 'target': '退会', 'value': 10},
{'source': 'A_20x1', 'target': '退会', 'value': 20},
{'source': 'B_20x1', 'target': '退会', 'value': 30},
{'source': '新規', 'target': '退会', 'value': 10},
]
#オブジェクト化
w = SankeyWidget(links=links, margins=dict(top=0, bottom=0, left=50, right=100))
#表示
w
linksの書き方で気をつけるのは20×1年のSランクと20×2年のランクを区別する必要があります。従ってS_20x1の表記をします。
順序があるもの
web遷移の様に順序があるものは同じコンテンツでもTOPから何回目の遷移かで分けて表記します。以下はTOPから入りコンテンツが2つあるタイプのHPの遷移をlinksで定義した例です。
from ipysankeywidget import SankeyWidget
#以下が流入出のデータ
links = [
{'source': 'TOP', 'target': 'CONTENTS11', 'value': 5000, 'color': 'red'},
{'source': 'TOP', 'target': 'CONTENTS21', 'value': 10000},
{'source': 'TOP', 'target': 'CONTENTS31', 'value': 7000, 'color': 'green'},
{'source': 'TOP', 'target': 'CONTENTS41', 'value': 10000, 'color': 'yellow'},
{'source': 'CONTENTS11', 'target': 'CONTENTS12', 'value': 2500, 'color': 'red'},
{'source': 'CONTENTS11', 'target': 'CONTENTS22', 'value': 500},
{'source': 'CONTENTS11', 'target': 'CONTENTS32', 'value': 800, 'color': 'green'},
{'source': 'CONTENTS11', 'target': 'CONTENTS42', 'value': 1200, 'color': 'yellow'},
{'source': 'CONTENTS21', 'target': 'CONTENTS12', 'value': 6000, 'color': 'red'},
{'source': 'CONTENTS21', 'target': 'CONTENTS22', 'value': 2000},
{'source': 'CONTENTS21', 'target': 'CONTENTS32', 'value': 1500, 'color': 'green'},
{'source': 'CONTENTS21', 'target': 'CONTENTS42', 'value': 500, 'color': 'yellow'},
{'source': 'CONTENTS31', 'target': 'CONTENTS12', 'value': 1000, 'color': 'red'},
{'source': 'CONTENTS31', 'target': 'CONTENTS22', 'value': 2500},
{'source': 'CONTENTS31', 'target': 'CONTENTS32', 'value': 2000, 'color': 'green'},
{'source': 'CONTENTS31', 'target': 'CONTENTS42', 'value': 1500, 'color': 'yellow'},
{'source': 'CONTENTS41', 'target': 'CONTENTS12', 'value': 6000, 'color': 'red'},
{'source': 'CONTENTS41', 'target': 'CONTENTS22', 'value': 500},
{'source': 'CONTENTS41', 'target': 'CONTENTS32', 'value': 500, 'color': 'green'},
{'source': 'CONTENTS41', 'target': 'CONTENTS42', 'value': 3000, 'color': 'yellow'},
]
w = SankeyWidget(links=links,margins=dict(top=0, bottom=0, left=50, right=200))
w
図示したものが上記です。分かりやすい様にcolor要素に色を渡して表示しています。
具体的な例と使い方について説明しました。次回はLayout等の細かい制御について説明します。