桑基图简介很多时候我们需要一种情况,我们必须可视化数据如何在实体之间流动。例如,考虑居民如何从一个国家迁移到另一个国家。这里展示了有多少居民从英格兰迁移到北爱尔兰、苏格兰和威尔士。从Sankey可视化中可以明显看出,从英格兰迁移到威尔士的居民比从苏格兰或北爱尔兰迁移的居民更多。什么是桑基图?Sankey图通常描述从一个实体(或节点)到另一个实体(或节点)的数据流。数据流向的实体称为节点。数据流起源的节点是源节点(例如左边的英格兰),流结束的节点是目标节点(例如右边的威尔士)。源节点和目标节点通常表示为带标签的矩形。流本身由称为链接的直线或弯曲路径表示。流/链接的宽度与流的体积/数量成正比。在上面的例子中,从英格兰到威尔士的流动(即常住移民)比从英格兰到苏格兰或北爱尔兰的流动(即常住移民)更广泛(更多),这表明更多的居民搬到威尔士而不是其他国家。桑基图可用于表示能量流、资金流、成本流以及任何具有流概念的事物。米纳尔关于拿破仑入侵俄罗斯的经典图表可能是最著名的桑基图示例。这种使用桑基图的可视化非常有效地显示了法国军队如何在前往俄罗斯和返回途中前进(或减少?)。在本文中,我们使用python的plotly来绘制Sankey图。如何绘制桑基图?本文使用2021年奥运会数据集绘制桑基图。此数据集包含有关奖牌总数的详细信息-国家/地区、奖牌总数以及金牌、银牌和铜牌的个人总数。我们画一个桑基图,看看一个国家获得了多少金牌、银牌和铜牌。df_medals=pd.read_excel("data/Medals.xlsx")print(df_medals.info())df_medals.rename(columns={'Team/NOC':'Country','Total':'TotalMedals','Gold':'GoldMedals','Silver':'SilverMedals','Bronze':'BronzeMedals'},inplace=True)df_medals.drop(columns=['Unnamed:7','Unnamed:8','RankbyTotal'],inplace=True)df_medalsRangeIndex:93entries,0to92Datacolumns(total9columns):#列非空计数Dtype--------------------------0Rank93非空int641Team/NOC93非空对象2Gold93非空int643Silver93非-nullint644铜牌93非空int645总计93非空int646按总数排名93非空int647未命名:70非空float648未命名:81非空float64dtypes:float64(2),int64(6),object(1)内存使用:6.7+KBNoneSankey图绘制库使用plotly的go.Sankey,它有2个参数——节点和链接(nodesandlinks)注意:所有节点——source和target都应该有唯一的标识符。就本文中的奥运奖牌数据集而言:来源是国家。将前3个国家(美国、中国和日本)视为源节点。使用以下(唯一)标识符、标签和颜色标记这些源节点:0:美国:绿色1:中国:蓝色2:日本:橙色目标是金、银或铜。使用以下(唯一)标识符、标签和颜色标记这些目标节点:3:Gold:Gold4:Silver:Silver5:Bronze:Brown链接(源节点和目标节点之间)是每种类型的奖牌数。在每个来源中有3个链接,每个链接都以一个目的地结束-金牌、银牌和铜牌。所以总共有9个链接。每个环节的宽度应为金牌、银牌和铜牌的数量。用以下来源标记这些指向目标、值和颜色的链接:0(美国)至3,4,5:39、41、331(中国)至3,4,5:38、32、182(日本)to3,4,5:27,14,17需要实例化2个pythondict对象来表示节点(源和目标):标签和颜色作为单独的列表和链接:源节点,目标节点,值(宽度)和颜色链接作为单独的列表并将其传递给plotly的go.Sankey。列表的每个索引(标签、源、目标、值和颜色)分别对应一个节点或链接。NODES=dict(#012345label=["美利坚合众国","中华人民共和国","日本","Gold","Silver","Bronze"],color=["seagreen","道奇蓝","橙色","金色","银色","棕色"],)LINKS=dict(source=[0,0,0,1,1,1,2,2,2],#链接开始或源节点目标=[3,4,5,3,4,5,3,4,5],#链接目标或目标节点值=[39,41,33,38,32,18,27,14,17],#链接的宽度(数量)#链接的颜色#目标节点:3-Gold4-Silver5-Bronzecolor=["lightgreen","lightgreen","lightgreen",#sourceNode:0-美国"lightskyblue","lightskyblue","lightskyblue",#Sourcenode:1-People'sRepublicofChina"bisque","bisque","bisque"],)#Sourcenode:2-Japandata=go.Sankey(node=NODES,link=LINKS)fig=go.Figure(data)fig.show()这是一个非常基础的Sankey图。但请注意图表太宽,银牌出现在金牌之前?下面介绍如何调整节点的位置和宽度。调整节点位置和图形宽度将x和y位置添加到节点以明确指定节点的位置。值应介于0和1之间。NODES=dict(#012345label=["UnitedStatesofAmerica","People'sRepublicofChina","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="奥运会-2021:Country&Medals",font_size=16)fig.show()然后得到一个紧凑的桑基图:下面我们看看代码中传递的各种参数是如何映射到图中的节点和链接的。代码如何映射到Sankey图添加有意义的悬停标签我们都知道plotlyplots是交互式的,我们可以将鼠标悬停在节点和链接上以获取更多信息。带有默认悬停标签的Sankey图将鼠标悬停在图表上时,将显示详细信息。悬停标签中显示的信息是默认文本:节点、节点名称、传入流、传出流和总值。例如:nodeUSA总共获得了11枚奖牌(=39gold+41silver+33bronze)Nodegold总共获得了104枚奖牌(=US39,中国38,日本27)如果我们觉得这些标签太冗长,我们可以改进这个过程。使用hovertemplate参数改进hoverlabels的格式对于节点,由于hoverlabels没有提供新信息,通过传递一个空的hovertemplate=""来删除hoverlabel对于链接,可以使标签简洁,格式为-Fornodesandlinks,让我们使用后缀“奖牌”显示值。例如,113个奖牌而不是113个。这可以通过使用具有适当值格式和值后缀的update_traces函数来实现。NODES=dict(#012345label=["美利坚合众国","中华人民共和国","日本","Gold","Silver","Bronze"],color=["seagreen",“道奇蓝”,“橙色”,“金色”,“银色”,“棕色”],x=[0,0,0,0.5,0.5,0.5],y=[0,0.5,1,0.1,0.5,1],hovertemplate="",)LINK_LABELS=[]["USA","China","Japan"]中的国家:["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],#链接目的地或目标节点值=[39,41,33,38,32,18,27,14,17],#链接宽度(amount)#链接的颜色#目标节点:3-Gold4-Silver5-Bronzecolor=["lightgreen","lightgreen","lightgreen",#Sourcenode:0-USA"lightskyblue","lightskyblue","lightskyblue",#Sourcenode:1-中国"bisque","bisque","bisque"],#源节点:2-日本label=LINK_LABELS,hovertemplate="%{label}",)data=go.Sankey(node=NODES,link=LINKS)fig=go.Figure(data)fig.update_layout(title="2021年奥运会:国家和奖牌",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("png")#fig.show()带有改进的悬停标签的桑基图泛化到多个节点和级别关于链接,节点称为源和目标作为链接目标的节点可以是另一个链接的源。该代码可以推广以处理数据集中的所有国家/地区。该图还可以扩展到另一个级别,以可视化按国家/地区划分的奖牌总数。NUM_COUNTRIES=5X_POS,Y_POS=0.5,1/(NUM_COUNTRIES-1)NODE_COLORS=["seagreen","dodgerblue","orange","palevioletred","darkcyan"]LINK_COLORS=["lightgreen","lightskyblue","浓汤”,“粉红色”,“浅青色”]source=[]node_x_pos,node_y_pos=[],[]node_labels,node_colors=[],NODE_COLORS[0:NUM_COUNTRIES]link_labels,link_colors,link_values=[],[],[]#第一组链接和节点foriinrange(NUM_COUNTRIES):source.extend([i]*3)node_x_pos.append(0.01)node_y_pos.append(round(i*Y_POS+0.01,2))country=df_medals['Country'][i]node_labels.append(country)获得["Gold","Silver","Bronze"]奖牌:link_labels.append(f"{country}-{medal}")link_values.append(df_medals[f"{medal}奖牌"][i])link_colors.extend([LINK_COLORS[i]]*3)source_last=max(source)+1target=[source_last,source_last+1,source_last+2]*NUM_COUNTRIEStarget_last=max(target)+1node_labels.extend(["Gold","Silver","Bronze"])node_colors.extend(["gold","silver","brown"])node_x_pos.extend([X_POS,X_POS,X_POS])node_y_pos.extend([0.01,0.5,1])#最后一组链接和节点source.extend([source_last,source_last+1,source_last+2])target.extend([target_last]*3)node_labels.extend(["TotalMedals"])node_colors.extend(["grey"])node_x_pos.extend([X_POS+0.25])node_y_pos.extend([0.5])对于["Gold","Silver","Bronze"]中的奖牌:link_labels.append(f"{medal}")link_values.append(df_medals[f"{medal}Medals"][:i+1].sum())link_colors.extend(["gold","silver","brown"])print("node_labels",node_labels)print("node_x_pos",node_x_pos);print("node_y_pos",node_y_pos)node_labels['美利坚合众国',"中华人民共和国",'日本','英国','中华民国','金牌','银牌','铜牌','奖牌总数']node_x_pos[0.01,0.01,0.01,0.01,0.01,0.5,0.5,0.5,0.75]node_y_pos[0.01,0.26,0.51,0.76,1.01,0.01,0.5,1,0.5]#显示NODES=字典(垫=20,厚度=20,线=字典(color="lightslategrey",width=0.5),hovertemplate="",label=node_labels,color=node_colors,x=node_x_pos,y=node_y_pos,)LINKS=dict(source=source,target=target,value=link_values,标签=link_labels,color=link_colors,hovertemplate="%{label}",)data=go.Sankey(arrangement='snap',node=NODES,link=LINKS)fig=go.Figure(data)fig.update_traces(值格式='3d',valuesuffix='Medals',selector=dict(type='sankey'))fig.update_layout(title="Olympics-2021:Country&Medals",font_size=16,width=1200,height=500,)fig.update_layout(hoverlabel=dict(bgcolor="grey",font_size=14,font_family="Rockwell"))fig.show("png")