数据可视化:桑基图的应用

在数据分析中,经常需要展示数据在不同实体之间的流动情况。例如,分析英国内部不同国家居民的迁移情况,观察从英格兰迁移到北爱尔兰、苏格兰和威尔士的居民数量。通过桑基图这种数据可视化方式,可以直观地看到数据流动的规模和方向。

桑基图简介

桑基图是一种特殊类型的流图,它通过宽度不等的箭头直观地表示不同节点(实体)之间的流动量。在桑基图中,数据流动的起点被称为源节点,终点被称为目标节点。节点通常以带标签的矩形表示,而流动本身则由直线或曲线路径表示,称为链接。链接的宽度与流动量成正比。例如,在上述居民迁移的例子中,从英格兰到威尔士的流动宽度大于到苏格兰或北爱尔兰的流动,表明迁移到威尔士的居民数量多于其他地区。

桑基图不仅可以用于展示居民迁移,还可以用于展示能量、金钱、成本等任何具有流动概念的数据。拿破仑入侵俄罗斯的经典桑基图是最著名的例子之一,它有效地展示了法国军队在前往俄罗斯及返回途中的进展(或减少)。

如何使用PythonPlotly绘制桑基图

以2021年奥运会奖牌数据为例,可以绘制一个桑基图来理解各国赢得的金牌、银牌和铜牌的数量。首先,需要使用Pandas库加载数据,并进行一些基本的数据预处理。

import pandas as pd df_medals = pd.read_excel("Medals.xlsx") print(df_medals.info()) df_medals.rename(columns={'Team/NOC':'Country', 'Total': 'Total Medals', 'Gold':'Gold Medals', 'Silver': 'Silver Medals', 'Bronze': 'Bronze Medals'}, inplace=True) print(df_medals)

接下来,将使用Plotly的go接口中的Sankey方法来绘制桑基图。这个方法需要两个参数:节点和链接。所有节点,无论是源节点还是目标节点,都应该有唯一的标识符。

在这个例子中,源节点是国家,选择美国、中国和日本作为源节点,并为这些源节点分配以下(唯一的)标识符、标签和颜色:

  • 0: 美国 - 绿色
  • 1: 中国 - 蓝色
  • 2: 日本 - 橙色

目标节点是金、银、铜奖牌。为这些目标节点分配以下(唯一的)标识符、标签和颜色:

  • 3: 金牌 - 金色
  • 4: 银牌 - 银色
  • 5: 铜牌 - 棕色

链接(源节点和目标节点之间的连接)将是每种奖牌(金、银、铜)的数量。从每个源节点,将有3个链接出发,每个链接都指向目标节点——金、银、铜。因此,将总共有9个链接。每个链接的宽度应等于金、银、铜奖牌的数量。将为这些链接分配以下源到目标、值和颜色:

  • 0(美国)到3,4,5: 39, 41, 33
  • 1(中国)到3, 4, 5: 38, 32, 18
  • 2(日本)到3,4,5: 27, 14, 17

需要创建两个Python字典对象来表示节点(包括源节点和目标节点):带有标签和颜色的单独列表,以及链接:源节点、目标节点、值(宽度)和链接的颜色作为单独的列表,并将这些传递给Plotly的go接口Sankey。

NODES = dict( 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], target=[3, 4, 5, 3, 4, 5, 3, 4, 5], value=[39, 41, 33, 38, 32, 18, 27, 14, 17], color=["lightgreen", "lightgreen", "lightgreen", "lightskyblue", "lightskyblue", "lightskyblue", "bisque", "bisque", "bisque"], ) data = go.Sankey(node=NODES, link=LINKS) fig = go.Figure(data) fig.show()

这是一个非常基础的桑基图。但有没有注意到图表太宽了,而且银牌出现在金牌之前?让调整节点的位置和图表的宽度。

NODES = dict( 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) fig.show()

通过这种方式,得到了一个紧凑的图表。

以下是代码中传递的各种参数如何映射到图表中的节点和链接:

桑基图是交互式的。可以悬停在节点和链接上以获取更多信息。

目前,悬停标签中显示的信息是默认文本。当悬停在节点上时,会显示节点名称、进入流量的数量、外出流量的数量和总值。例如,节点“美国”总共有113枚奖牌(39金+41银+33铜)。节点“金牌”总共有104枚奖牌(来自美国的39枚,来自中国的38枚,来自日本的27枚)。对于链接,会显示源节点名称、目标节点名称和链接的值。例如,从源节点美国到目标节点银牌的链接有39枚奖牌。

难道不认为这些标签太冗长了吗?所有这些都可以改进。让使用hovertemplate参数改进悬停标签的格式。对于节点,由于悬停标签没有提供比已经存在的信息更多的信息,可以通过传递一个空的hovertemplate=" "来关闭悬停标签。对于链接,可以将标签格式简化为<国家>-<奖牌类型>。对于节点和链接,可以在显示值时添加后缀“奖牌”。例如,113奖牌而不是仅仅113。这可以通过使用update_traces函数和适当的valueformat和valuesuffix来实现。

NODES = dict( 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], target=[3, 4, 5, 3, 4, 5, 3, 4, 5], value=[39, 41, 33, 38, 32, 18, 27, 14, 17], color=["lightgreen", "lightgreen", "lightgreen", "lightskyblue", "lightskyblue", "lightskyblue", "bisque", "bisque", "bisque"], 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) 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()

桑基图——带有改进的悬停标签

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485