視覺化數據-桑基圖

Raven
隨筆趣事
Published in
11 min readJul 8, 2023
Photo by Rod Long on Unsplash

介紹

很多時候,我們必須可視化數據在實體之間的流動方式。 例如,讓我們以居民如何從英國境內的一個國家遷移到另一個國家為例。

在這裡,了解有多少居民從英格蘭遷移到北愛爾蘭、蘇格蘭和威爾士將是一個有趣的分析。

從桑基圖中可以明顯看出,從英格蘭遷移到威爾士的居民多於遷移到蘇格蘭或北愛爾蘭的居民。

什麼是桑基圖?

展現數據流動的利器,比如材料、能源、成本等等。

主要由邊、流量、結點組成:

  • 來源(source): 數據(邊越大數值越大)
  • 流量(value): 數據的具體數值
  • 終點(target): 不同分類

圖片

桑基圖自命名那一刻起,就注定和能量分不開了。桑基圖通常用於視覺化能源或成本轉移,也可以幫助我們確定各部分流量在總體的大概佔比。

無論數據怎麼流動,桑基圖的總數值保持不變,堅持能量守恆。

種類

不外乎就是這些,遵守能量守恆。

使用Python實作

為了繪製桑基圖,我們同樣使用Olympics 2021

該數據集包含有關獎牌統計的詳細資訊 : 國家、獎牌總數以及金牌、銀牌和銅牌的分配情況。

讓我們繪製一個桑基圖來了解一個國家獲得的獎牌中有多少是金牌、銀牌和銅牌:

我們將使用 plotly 的 go interface ,它有2個參數,nodes和link。

請注意,所有nodes(source和target)都應具有唯一id。

在這個分析中:

Source就是國家。 讓我們觀察前 3 個國家(美國、中國和日本)作為來源節點。 讓我們用以下(唯一的)id、label和color來標記這些點:

0代表美國,綠色;1代表中國,藍色;2代表日本,橘色。

Target是金牌、銀牌和銅牌:

3代表金牌;4代表銀牌;代表銅牌。

LInk是每個國家的獎牌數量(金、銀、銅),對於每個來源,我們都會有 3 個起始鏈接,每個鏈接均以target結束 — 金級、銀級和銅級。 所以我們總共有 9 個鏈接

每個鏈接的寬度應為金、銀、銅牌的數量。 讓我們用以下來標記這些鏈接:

0 (USA) to 3,4,5 : 39, 41, 33

1 (China) to 3, 4, 5 : 38, 32, 18

2 (Japan) to 3,4,5 : 27, 14, 17

我們需要實例化 2 個 python dict 來表示:

  • Nodes(來源節點和目標節點):將標籤和顏色作為單獨的列表,
  • Links:來源節點、目標節點、值(寬度)以及作為單獨列表的鏈接的顏色
NODES = dict( #           0                               1                          2        3       4           5
label = ["United States of America", "People's Republic of China", "Japan", "Gold", "Silver", "Bronze"],
color = ["seagreen", "dodgerblue", "orange", "gold", "silver", "brown" ],)

LINKS = dict( source = [ 0, 0, 0, 1, 1, 1, 2, 2, 2], # The origin or the source nodes of the link
target = [ 3, 4, 5, 3, 4, 5, 3, 4, 5], # The destination or the target nodes of the link
value = [ 39, 41, 33, 38, 32, 18, 27, 14, 17], # The width (quantity) of the links
# Color of the links
# Target Node: 3-Gold 4 -Silver 5-Bronze
color = [ "lightgreen", "lightgreen", "lightgreen", # Source Node: 0 - United States of America
"lightskyblue", "lightskyblue", "lightskyblue", # Source Node: 1 - People's Republic of China
"bisque", "bisque", "bisque"],) # Source Node: 2 - Japan

data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.update_layout(title="Olympics - 2021: Country & Medals", font_size=16)
fig.show("png") #fig.show()
Basic Sankey diagram

這裡我們有一個非常基本的圖。 但您是否注意到圖表太寬並且白銀出現在黃金之前? 讓我們調整節點的位置和寬度。

調整節點位置和圖表寬度

讓我們添加節點的 x 和 y 位置以指定節點的位置。 值應介於 0 和 1 之間。

NODES = dict( #           0                               1                          2        3       4           5
label = ["United States of America", "People's Republic of China", "Japan", "Gold", "Silver", "Bronze"],
color = [ "seagreen", "dodgerblue", "orange", "gold", "silver", "brown" ],
x = [ 0, 0, 0, 0.5, 0.5, 0.5],
y = [ 0, 0.5, 1, 0.1, 0.5, 1],)
data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.update_layout(title="Olympics - 2021: Country & Medals", font_size=16, width=1200,height=500,)
ig.show()
Adjusted Sankey diagram

請參閱下面的程式碼傳遞的各種參數如何映射到圖中的節點和鏈接:

how code maps to diagram

添加有意義的懸浮標籤

圖是可互動的。 您可以將鼠標懸停在節點和鏈接上以獲取更多訊息。

with default hover labels

目前,懸停標籤中顯示的訊息是默認文字。 當您將滑鼠懸停在

  • Nodes: 節點名稱、流入數量、流出數量、合計值。 例如:美國共有 11 枚獎牌(= 39 枚金牌 + 41 枚銀牌 + 33 枚銅牌)
  • Node 金牌 共有 104 枚獎牌(= 39 枚來自美國,38 枚來自中國,27 枚來自日本)
  • Links : 來源節點名稱和目標節點名稱以及鏈接的值。 例如,從 USA 到目標節點 Silver 的鏈接有 39 枚獎牌。

你不覺得標籤太冗長了嗎? 這些都是可以改進的。

讓我們使用hovertemplate參數改進懸停標籤的格式:

NODES = dict( #           0                               1                          2        3       4           5
label = ["United States of America", "People's Republic of China", "Japan", "Gold", "Silver", "Bronze"],
color = [ "seagreen", "dodgerblue", "orange", "gold", "silver", "brown" ],
x = [ 0, 0, 0, 0.5, 0.5, 0.5],
y = [ 0, 0.5, 1, 0.1, 0.5, 1],
hovertemplate=" ",)

LINK_LABELS = []
for country in ["USA","China","Japan"]:
for medal in ["Gold","Silver","Bronze"]:
LINK_LABELS.append(f"{country}-{medal}")
LINKS = dict( source = [ 0, 0, 0, 1, 1, 1, 2, 2, 2], # The origin or the source nodes of the link
target = [ 3, 4, 5, 3, 4, 5, 3, 4, 5], # The destination or the target nodes of the link
value = [ 39, 41, 33, 38, 32, 18, 27, 14, 17], # The width (quantity) of the links
# Color of the links
# Target Node: 3-Gold 4 -Silver 5-Bronze
color = [ "lightgreen", "lightgreen", "lightgreen", # Source Node: 0 - United States of America
"lightskyblue", "lightskyblue", "lightskyblue", # Source Node: 1 - People's Republic of China
"bisque", "bisque", "bisque"], # Source Node: 2 - Japan
label = LINK_LABELS, hovertemplate="%{label}",)

data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.update_layout(title="Olympics - 2021: Country & Medals", font_size=16, width=1200,height=500,)
fig.update_traces( valueformat='3d', valuesuffix=' Medals', selector=dict(type='sankey'))
fig.update_layout(hoverlabel=dict(bgcolor="lightgray",font_size=16,font_family="Rockwell"))
fig.show()
Sankey diagram — with improved hover labels

範例程式碼

結論

我們了解瞭如何使用桑基圖有效地表示流量,以及如何使用 python 實作桑基圖。

優點:

  • 可同時呈現出資訊流的狀況、以及資料的組成比例(數值、類別)

缺點:

  • 數據太多,不同的邊相互重疊,會降低了桑基圖的可讀性。
  • 有觀測指標的值差異太,會降低了桑基圖的可讀性。
  • 若觀測指標的值差異太,也會降低了桑基圖的可讀性。

我的個人資訊

--

--

Raven
隨筆趣事

熱愛寫code,平時遇到有趣的事情,也會上來寫寫,抒發自己的心情。