I also created a super simple Sankey diagram drawing function before, but it can't deal with many special cases. Here I refactored some of it and wrote it into a class, adding a lot of built-in decoration functions to realize that the inflow and outflow data are not equal or Drawing of special cases of cross-layer data flow, let's first show what effect can be achieved by using the function I wrote:
The above pictures are all examples of this article, let’s explain how to use it first, and put the tool functions at the end of the article! ! !
Usage introduction
0 data entry
Data input requires a three-column cell array, which respectively represent the starting point of the Source flow direction, the end point of the Target flow direction, and the Value is the amount of data flowing:
links={
'a1','A',1.2;
'a2','A',1;
'a1','B',.6;
'a3','A',1;
'a3','C',.5;
'b1','B',.4;
'b2','B',1;
'b3','B',1;
'c1','C',1;
'c2','C',1;
'c3','C',1;
'A','AA',2;
'A','BB',1.2;
'B','BB',1.5;
'B','AA',1.5;
'C','BB',2.3;
'C','AA',1.2};
1 Basic drawing
In fact, there are mainly two lines to draw:
links={
'a1','A',1.2;'a2','A',1;'a1','B',.6;'a3','A',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','AA',1.5; 'C','BB',2.3; 'C','AA',1.2};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 开始绘图(Start drawing)
SK.draw()
2 Connection rendering method
Connection rendering methods are:
- ‘left’
- ‘right’
- ‘interp’(default)
- ‘map’
- ‘simple’
Among them, the gradient rendering interp is the default rendering method, and the rendering method can be modified by setting attributes before draw RenderingMethod
, for example (or the previous data):
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='left';
% 开始绘图(Start drawing)
SK.draw()
left
That is, the connection color is the same as the left node square:
right
interp
map
That is, coloring is performed according to the value, and the color bar can be changed through the colormap function.
simple
Pure gray:
3 block alignment
It is to set the upper alignment, lower alignment, and centering of 'up'/'down'/'center' (default) respectively, and configure it through the settings. It Align
still needs to be set before draw:
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='up';
% 开始绘图(Start drawing)
SK.draw()
up
down
center
4 text position
The text position is nothing more than centering up, down, left, and right: 'left'(default)/'right'/'top'/'center'/'bottom', set through attributes LabelLocation
:
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='top';
SK.Sep=.2;
% 开始绘图(Start drawing)
SK.draw()
Note that when the position is set to top and down, in order to avoid occlusion, you can set Sep
and adjust the proportion of the space between the blocks:
left
right
top
center
bottom
5 Gap and Proportion
The proportion of the horizontal width of the square can be set to be greater than 0 and less than 1 by setting BlockScale
the property. The larger the proportion, the greater the proportion of the square.
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 设置方块占比(Set the scale of blocks)
% BlockScale>0 & BlockScale<1
SK.BlockScale=.4;
% 开始绘图(Start drawing)
SK.draw()
If adjusted to .001:
At the same time, adjust the ratio of the upper and lower gaps:
% 设置缝隙占比(Separation distance proportion)
SK.Sep=.4;
6 color setting
Change the preset color by setting ColorList
properties:
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 设置颜色(Set color)
SK.ColorList=[0.46, 0.54, 0.46;
0.54, 0.68, 0.46;
0.41, 0.49, 0.36;
0.38, 0.53, 0.84;
0.44, 0.59, 0.87;
0.58, 0.79, 0.93;
0.65, 0.64, 0.84;
0.63, 0.63, 0.80;
0.56, 0.53, 0.67;
0.76, 0.81, 0.43;
0.56, 0.86, 0.97;
0.78, 0.59, 0.65;
0.89, 0.91, 0.53;
0.93, 0.56, 0.25;];
% 开始绘图(Start drawing)
SK.draw()
Of course, you can only change part of the color scheme (for example, change a3 to black):
links={
'a1','A',1.2;'a2','A',1;'a1','B',.6;'a3','A',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','AA',1.5; 'C','BB',2.3; 'C','AA',1.2};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 设置颜色(Set color)
SK.ColorList(3,:)=[0,0,0];
% 开始绘图(Start drawing)
SK.draw()
7 Incoming and outgoing data are not equal
The inflow and outflow data are not equal so that special graphs can also be drawn. The original data is changed from C to BB to 0.5 (links{16,3}=.5):
% 流入及流出数据不相等(Unequal inflow and outflow data)
links={
'a1','A',1.2;'a2','A',1;'a1','B',.6;'a3','A',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','AA',1.5; 'C','BB',2.3; 'C','AA',1.2};
links{
16,3}=.5;
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 开始绘图(Start drawing)
SK.draw();
8 Flow across levels
% 含跨层级流动(Including cross level flow)
links={
'a1','A',1.2;'a2','A',2;'a1','B',.6;'a3','D',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','D',1.5; 'C','BB',2.3; 'C','AA',1.2;
'D','AA',1.4; 'D','BB',1.1};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
SK.Sep=.1;
% 开始绘图(Start drawing)
SK.draw()
In this way, there will be occlusion when drawing directly, and there will be a rather awkward connection sequence (the connection at the bottom goes to the top, and the connection at the top goes to the bottom)
The default connection order is
unique([Source;Target],'stable')
Calculated in this way, of course, this property can be set by yourself, we first adjust the total order of connections:
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改节点排列次序(Modify node arrangement order)
SK.NodeList={
'a3','a1','a2','b1','b2','b3','c1','c2','c3','D','A','B','C','AA','BB'};
SK.Sep=.1;
% 开始绘图(Start drawing)
SK.draw()
It is more pleasing to the eye, but it is still blocked in the middle. If we want to move it up, we can use the moveBlockY function, where the D node is the 10th node, and we will move it up by 6 unit values (if the Value is large Just move more), note that because the thing to be moved is something that has already been drawn, so the moving operation should be placed after the draw operation:
% 含跨层级流动(Including cross level flow)
links={
'a1','A',1.2;'a2','A',2;'a1','B',.6;'a3','D',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','D',1.5; 'C','BB',2.3; 'C','AA',1.2;
'D','AA',1.4; 'D','BB',1.1};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改节点排列次序(Modify node arrangement order)
SK.NodeList={
'a3','a1','a2','b1','b2','b3','c1','c2','c3','D','A','B','C','AA','BB'};
SK.Sep=.1;
% 开始绘图(Start drawing)
SK.draw()
% 修改节点Y轴位置变化(Modify the position change of node Y direction)
SK.moveBlockY(10,+6);
9 Graphic decoration has been drawn
To modify the block, use the form of setBlock(n,prop,…), for example (add a black thick outline to block No. 2):
Because what needs to be modified is something that has already been drawn, so the modification operation should be placed after the draw operation:
links={
'a1','A',1.2;'a2','A',1;'a1','B',.6;'a3','A',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','AA',1.5; 'C','BB',2.3; 'C','AA',1.2};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 开始绘图(Start drawing)
SK.draw()
% 设置方块属性(Set Block Properties)
SK.setBlock(2,'EdgeColor',[0,0,0],'LineWidth',6)
Modify the cycle (all set to gray):
% 循环设置方块属性(Loop Set Block Properties)
for i=1:14
SK.setBlock(i,'FaceColor',[.5,.5,.5])
end
To set the link string use setLink(n,prop,…)
% 设置连接属性(Set Link Properties)
SK.setLink(5,'FaceColor',[0,0,0],'FaceAlpha',.5)
set text setLabel(n,prop,...)
% 设置标签属性(Set Label Properties)
SK.setLabel(11,'FontSize',40,'Color',[0,0,.8])
By the way, just add the title or the title function:
title(gca,'sankey plot by slandarer','FontSize',30,'FontName','Cambria')
10 Ring Sankey Diagram
The function of this article can be linked with the directed graph function I wrote to create a circular Sankey graph. The function of the directed graph biChordChart
can be obtained from the fileexchange or gitee warehouse, and the length of the problem will not be repeated:
biChordChart directed chord graph fileexchange
Zhaoxu Liu / slandarer (2023). Digraph chord chart ( https://www.mathworks.com/matlabcentral/fileexchange/121043-digraph-chord-chart ), MATLAB Central File Exchange. Retrieved from 2023/4/ 1.
gitee warehouse
https://gitee.com/slandarer/matlab-chord-chart
Unlicensed codes cannot be used for any commercial purposes, the reference can refer to the above fileexchange link, and the reference text format can be changed when citing, but at least the link should be included.
Linkage use code
links={
'a1','A',1.2;'a2','A',1;'a1','B',.6;'a3','A',1; 'a3','C',0.5;
'b1','B',.4; 'b2','B',1;'b3','B',1; 'c1','C',1;
'c2','C',1; 'c3','C',1;'A','AA',2; 'A','BB',1.2;
'B','BB',1.5; 'B','AA',1.5; 'C','BB',2.3; 'C','AA',1.2};
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
SK.draw();close all
figure('Name','sankey demo6','Units','normalized','Position',[.05,.05,.59,.8])
BCC=biChordChart(SK.AdjMat,'Arrow','on','Label',SK.NodeList);
BCC.CData=[[65,140,240;252,180,65;224,64,10;5,100,146;191,191,191;26,59,105;255,227,130;18,156,221;
202,107,75;0,92,219;243,210,136;80,99,129;241,185,168;224,131,10;120,147,190]./255;
[127,91,93;187,128,110;197,173,143;59,71,111;104,95,126;76,103,86;112,112,124;
72,39,24;197,119,106;160,126,88;238,208,146]./255];
BCC=BCC.draw();
% 添加刻度
BCC.tickState('on')
BCC.tickLabelState('on')
BCC.setTickFont('FontName','Cambria','FontSize',11)
BCC.setFont('FontName','Cambria','FontSize',17)
BCC.setLabelRadius(1.32);
11 Additional small case (1)
It should be noted that the node here is in Chinese, and the font must be set to a font that supports Chinese, such as Song Ti.
clc;clear;
links{
7,3}='';
for i=1:7
links{
i,1}=['浏览',num2str(i)];
links{
i,2}=['浏览',num2str(i+1)];
links{
i,3}=10000-1400*i;
end
for i=1:7
links{
i+7,1}=['浏览',num2str(i)];
links{
i+7,2}=['下载',num2str(i)];
links{
i+7,3}=900;
end
for i=1:7
links{
i+14,1}=['浏览',num2str(i)];
links{
i+14,2}=['流失',num2str(i)];
links{
i+14,3}=500;
if i>=3
links{
i+14,3}=1100;
end
end
for i=1:6
links{
i+21,1}=['下载',num2str(i)];
links{
i+21,2}=['浏览',num2str(i+2)];
links{
i+21,3}=600;
end
for i=1:6
links{
i+27,1}=['下载',num2str(i)];
links{
i+27,2}=['流失',num2str(i+1)];
links{
i+27,3}=300;
end
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
SK.NodeList={
'浏览1','浏览2','浏览3','浏览4','浏览5','浏览6','浏览7','浏览8',...
'下载1','下载2','下载3','下载4','下载5','下载6','下载7',...
'流失1','流失2','流失3','流失4','流失5','流失6','流失7'};
SK.ColorList=[197,141,91;69,168,134;114,191,220;193,135,146;242,132,98;249,190,89;207,202,100;171,203,110;
repmat([114,158,158],[7,1]);repmat([100,136,177],[7,1])]./255;
% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='down';
% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='left';
% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='right';
% 设置方块占比(Set the scale of blocks)
% BlockScale>0 & BlockScale<1
SK.BlockScale=.16;
% 开始绘图(Start drawing)
SK.draw()
% 循环设置标签属性(Loop Set Label Properties)
for i=1:22
SK.setLabel(i,'FontName','宋体','FontSize',12)
end
for i=10:15
SK.moveBlockY(i,(9-i).*1000);
end
for i=17:22
SK.moveBlockY(i,(16-i).*1000);
end
The effect of the three alignment methods:
In fact, the basic version without any additional parameter adjustment is also very good-looking:
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
SK.NodeList={
'浏览1','浏览2','浏览3','浏览4','浏览5','浏览6','浏览7','浏览8',...
'下载1','下载2','下载3','下载4','下载5','下载6','下载7',...
'流失1','流失2','流失3','流失4','流失5','流失6','流失7'};
% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='top';
% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='left';
% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='right';
% 开始绘图(Start drawing)
SK.draw()
% 循环设置标签属性(Loop Set Label Properties)
for i=1:22
SK.setLabel(i,'FontName','宋体','FontSize',12)
end
12 Additional small case (2)
% 随机生成数据(Randomly generated data)
clc;clear;
SourceValue=randi([1,30],[1,9]);
LayerNum=[9,6,4,7,10];
links{
1,3}='';
for k=1:4
TargetValue=zeros(1,LayerNum(k+1));
for i=1:LayerNum(k)
tValue=randi([0,13],[1,LayerNum(k+1)]);
tValue=tValue./sum(tValue).*SourceValue(i);
for j=1:LayerNum(k+1)
TargetValue(j)=TargetValue(j)+tValue(j);
if tValue(j)>eps
tLen=size(links,1);
links{
tLen+1,1}=[char(64+k),num2str(i)];
links{
tLen+1,2}=[char(64+k+1),num2str(j)];
links{
tLen+1,3}=tValue(j);
end
end
end
SourceValue=TargetValue;
end
links(1,:)=[];
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));
% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='interp';
% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='center';
% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='top';
% 设置缝隙占比(Separation distance proportion)
SK.Sep=.4;
% 开始绘图(Start drawing)
SK.draw()
You can adjust the rendering method yourself:
Tool function complete code
classdef SSankey < handle
% Copyright (c) 2023, Zhaoxu Liu / slandarer
% =========================================================================
% @author : slandarer
% 公众号 : slandarer随笔
% 知乎 : slandarer
% -------------------------------------------------------------------------
% Zhaoxu Liu / slandarer (2023). sankey plot
% (https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot),
% MATLAB Central File Exchange. 检索来源 2023/4/28.
properties
Source;Target;Value;
SourceInd;TargetInd;
Layer;LayerPos;
AdjMat;BoolMat;
RenderingMethod='interp' % 'left'/'right'/'interp'/'map'/'simple'
LabelLocation='left' % 'left'/'right'/'top'/'center'/'bottom'
Align='center' % 'up'/'down'/'center'
BlockScale=0.05; % BlockScale>0 ! !
Sep=0.05; % Sep>=0 ! !
NodeList={
};
ColorList=[[65,140,240;252,180,65;224,64,10;5,100,146;191,191,191;26,59,105;255,227,130;18,156,221;
202,107,75;0,92,219;243,210,136;80,99,129;241,185,168;224,131,10;120,147,190]./255;
[127,91,93;187,128,110;197,173,143;59,71,111;104,95,126;76,103,86;112,112,124;
72,39,24;197,119,106;160,126,88;238,208,146]./255];
BlockHdl;LinkHdl;LabelHdl;ax;Parent;
BN;LN;VN;TotalLen;SepLen;
arginList={
'RenderingMethod','LabelLocation','BlockScale',...
'Sep','Align','ColorList','Parent','NameList'}
end
% 构造函数 =================================================================
methods
function obj=SSankey(varargin)
% 获取基本数据 -------------------------------------------------
if isa(varargin{
1},'matlab.graphics.axis.Axes')
obj.ax=varargin{
1};varargin(1)=[];
else
end
obj.Source=varargin{
1};
obj.Target=varargin{
2};
obj.Value=varargin{
3};
varargin(1:3)=[];
% 获取其他信息 -------------------------------------------------
for i=1:2:(length(varargin)-1)
tid=ismember(obj.arginList,varargin{
i});
if any(tid)
obj.(obj.arginList{
tid})=varargin{
i+1};
end
end
if isempty(obj.ax)&&(~isempty(obj.Parent)),obj.ax=obj.Parent;end
if isempty(obj.ax),obj.ax=gca;end
obj.ax.NextPlot='add';
% 基本数据预处理 -----------------------------------------------
if isempty(obj.NodeList)
obj.NodeList=[obj.Source;obj.Target];
obj.NodeList=unique(obj.NodeList,'stable');
end
obj.BN=length(obj.NodeList);
if length(obj.NodeList)>size(obj.ColorList,1)
obj.ColorList=[obj.ColorList;rand(length(obj.NodeList),3).*.7];
end
obj.VN=length(obj.Value);
% 坐标区域基础设置 ---------------------------------------------
obj.ax.YDir='reverse';
obj.ax.XColor='none';
obj.ax.YColor='none';
end
% Copyright (c) 2023, Zhaoxu Liu / slandarer
% =========================================================================
% @author : slandarer
% 公众号 : slandarer随笔
% 知乎 : slandarer
% -------------------------------------------------------------------------
% Zhaoxu Liu / slandarer (2023). sankey plot
% (https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot),
% MATLAB Central File Exchange. 检索来源 2023/4/28.
% 绘图函数 =================================================================
function draw(obj)
% 生成整体邻接矩阵 ---------------------------------------------
obj.AdjMat=zeros(obj.BN,obj.BN);
for i=1:length(obj.Source)
obj.SourceInd(i)=find(strcmp(obj.Source{
i},obj.NodeList));
obj.TargetInd(i)=find(strcmp(obj.Target{
i},obj.NodeList));
obj.AdjMat(obj.SourceInd(i),obj.TargetInd(i))=obj.Value{
i};
end
obj.BoolMat=abs(obj.AdjMat)>0;
% 计算每个对象位于的层、每层方块长度、每个方块位置 ----------------
obj.Layer=zeros(obj.BN,1);
obj.Layer(sum(obj.BoolMat,1)==0)=1;
startMat=diag(obj.Layer);
for i=1:(obj.BN-1)
tLayer=(sum(startMat*obj.BoolMat^i,1)>0).*(i+1);
obj.Layer=max([obj.Layer,tLayer'],[],2);
end
obj.LN=max(obj.Layer);
obj.TotalLen=max([sum(obj.AdjMat,1).',sum(obj.AdjMat,2)],[],2);
obj.SepLen=max(obj.TotalLen).*obj.Sep;
obj.LayerPos=zeros(obj.BN,4);
for i=1:obj.LN
tBlockInd=find(obj.Layer==i);
tBlockLen=[0;cumsum(obj.TotalLen(tBlockInd))];
tY1=tBlockLen(1:end-1)+(0:length(tBlockInd)-1).'.*obj.SepLen;
tY2=tBlockLen(2:end)+(0:length(tBlockInd)-1).'.*obj.SepLen;
obj.LayerPos(tBlockInd,3)=tY1;
obj.LayerPos(tBlockInd,4)=tY2;
% for j=1:length(tY2)
% plot([i,i],[tY1(j),tY2(j)],'LineWidth',2)
% end
end
obj.LayerPos(:,1)=obj.Layer;
obj.LayerPos(:,2)=obj.Layer+obj.BlockScale;
% 根据对齐方式调整Y坐标 -----------------------------------------
tMinY=min(obj.LayerPos(:,3));
tMaxY=max(obj.LayerPos(:,4));
for i=1:obj.LN
tBlockInd=find(obj.Layer==i);
tBlockPos3=obj.LayerPos(tBlockInd,3);
tBlockPos4=obj.LayerPos(tBlockInd,4);
switch obj.Align
case 'up'
case 'down'
obj.LayerPos(tBlockInd,3)=obj.LayerPos(tBlockInd,3)+tMaxY-max(tBlockPos4);
obj.LayerPos(tBlockInd,4)=obj.LayerPos(tBlockInd,4)+tMaxY-max(tBlockPos4);
case 'center'
obj.LayerPos(tBlockInd,3)=obj.LayerPos(tBlockInd,3)+...
min(tBlockPos3)/2-max(tBlockPos4)/2+tMinY/2-tMaxY/2;
obj.LayerPos(tBlockInd,4)=obj.LayerPos(tBlockInd,4)+...
min(tBlockPos3)/2-max(tBlockPos4)/2+tMinY/2-tMaxY/2;
end
end
% 绘制连接 -----------------------------------------------------
for i=1:obj.VN
tSource=obj.SourceInd(i);
tTarget=obj.TargetInd(i);
tS1=sum(obj.AdjMat(tSource,1:(tTarget-1)))+obj.LayerPos(tSource,3);
tS2=sum(obj.AdjMat(tSource,1:tTarget))+obj.LayerPos(tSource,3);
tT1=sum(obj.AdjMat(1:(tSource-1),tTarget))+obj.LayerPos(tTarget,3);
tT2=sum(obj.AdjMat(1:tSource,tTarget))+obj.LayerPos(tTarget,3);
if isempty(tS1),tS1=0;end
if isempty(tT1),tT1=0;end
tX=[obj.LayerPos(tSource,1),obj.LayerPos(tSource,2),obj.LayerPos(tTarget,1),obj.LayerPos(tTarget,2)];
qX=linspace(obj.LayerPos(tSource,1),obj.LayerPos(tTarget,2),200);qT=linspace(0,1,50);
qY1=interp1(tX,[tS1,tS1,tT1,tT1],qX,'pchip');
qY2=interp1(tX,[tS2,tS2,tT2,tT2],qX,'pchip');
XX=repmat(qX,[50,1]);YY=qY1.*(qT'.*0+1)+(qY2-qY1).*(qT');
MeshC=ones(50,200,3);
switch obj.RenderingMethod
case 'left'
MeshC(:,:,1)=MeshC(:,:,1).*obj.ColorList(tSource,1);
MeshC(:,:,2)=MeshC(:,:,2).*obj.ColorList(tSource,2);
MeshC(:,:,3)=MeshC(:,:,3).*obj.ColorList(tSource,3);
case 'right'
MeshC(:,:,1)=MeshC(:,:,1).*obj.ColorList(tTarget,1);
MeshC(:,:,2)=MeshC(:,:,2).*obj.ColorList(tTarget,2);
MeshC(:,:,3)=MeshC(:,:,3).*obj.ColorList(tTarget,3);
case 'interp'
MeshC(:,:,1)=repmat(linspace(obj.ColorList(tSource,1),obj.ColorList(tTarget,1),200),[50,1]);
MeshC(:,:,2)=repmat(linspace(obj.ColorList(tSource,2),obj.ColorList(tTarget,2),200),[50,1]);
MeshC(:,:,3)=repmat(linspace(obj.ColorList(tSource,3),obj.ColorList(tTarget,3),200),[50,1]);
case 'map'
MeshC=MeshC(:,:,1).*obj.Value{
i};
case 'simple'
MeshC(:,:,1)=MeshC(:,:,1).*.6;
MeshC(:,:,2)=MeshC(:,:,2).*.6;
MeshC(:,:,3)=MeshC(:,:,3).*.6;
end
obj.LinkHdl(i)=surf(obj.ax,XX,YY,XX.*0,'EdgeColor','none','FaceAlpha',.3,'CData',MeshC);
end
% 绘制方块 -----------------------------------------------------
for i=1:obj.BN
obj.BlockHdl(i)=fill(obj.ax,obj.LayerPos(i,[1,2,2,1]),...
obj.LayerPos(i,[3,3,4,4]),obj.ColorList(i,:),'EdgeColor','none');
end
% 绘制文本 -----------------------------------------------------
for i=1:obj.BN
switch obj.LabelLocation
case 'right'
obj.LabelHdl(i)=text(obj.ax,obj.LayerPos(i,2),mean(obj.LayerPos(i,[3,4])),...
[' ',obj.NodeList{
i}],'FontSize',15,'FontName','Times New Roman','HorizontalAlignment','left');
case 'left'
obj.LabelHdl(i)=text(obj.ax,obj.LayerPos(i,1),mean(obj.LayerPos(i,[3,4])),...
[obj.NodeList{
i},' '],'FontSize',15,'FontName','Times New Roman','HorizontalAlignment','right');
case 'top'
obj.LabelHdl(i)=text(obj.ax,mean(obj.LayerPos(i,[1,2])),obj.LayerPos(i,3),...
obj.NodeList{
i},'FontSize',15,'FontName','Times New Roman','HorizontalAlignment','center','VerticalAlignment','bottom');
case 'center'
obj.LabelHdl(i)=text(obj.ax,mean(obj.LayerPos(i,[1,2])),mean(obj.LayerPos(i,[3,4])),...
obj.NodeList{
i},'FontSize',15,'FontName','Times New Roman','HorizontalAlignment','center');
case 'bottom'
obj.LabelHdl(i)=text(obj.ax,mean(obj.LayerPos(i,[1,2])),obj.LayerPos(i,4),...
obj.NodeList{
i},'FontSize',15,'FontName','Times New Roman','HorizontalAlignment','center','VerticalAlignment','top');
end
end
% -------------------------------------------------------------
axis tight;help SSankey
end
% Copyright (c) 2023, Zhaoxu Liu / slandarer
% =========================================================================
% @author : slandarer
% 公众号 : slandarer随笔
% 知乎 : slandarer
% -------------------------------------------------------------------------
% Zhaoxu Liu / slandarer (2023). sankey plot
% (https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot),
% MATLAB Central File Exchange. 检索来源 2023/4/28.
% =========================================================================
function setBlock(obj,n,varargin)
set(obj.BlockHdl(n),varargin{
:})
end
function setLink(obj,n,varargin)
set(obj.LinkHdl(n),varargin{
:})
end
function setLabel(obj,n,varargin)
set(obj.LabelHdl(n),varargin{
:})
end
function moveBlockY(obj,n,dy)
obj.LayerPos(n,[3,4])=obj.LayerPos(n,[3,4])-dy;
set(obj.BlockHdl(n),'YData',obj.LayerPos(n,[3,3,4,4]));
switch obj.LabelLocation
case 'right',set(obj.LabelHdl(n),'Position',[obj.LayerPos(n,2),mean(obj.LayerPos(n,[3,4]))]);
case 'left',set(obj.LabelHdl(n),'Position',[obj.LayerPos(n,1),mean(obj.LayerPos(n,[3,4]))]);
case 'top',set(obj.LabelHdl(n),'Position',[mean(obj.LayerPos(n,[1,2])),obj.LayerPos(n,3)]);
case 'center',set(obj.LabelHdl(n),'Position',[mean(obj.LayerPos(n,[1,2])),mean(obj.LayerPos(n,[3,4]))]);
case 'bottom',set(obj.LabelHdl(n),'Position',[mean(obj.LayerPos(n,[1,2])),obj.LayerPos(n,4)]);
end
for i=1:obj.VN
tSource=obj.SourceInd(i);
tTarget=obj.TargetInd(i);
if tSource==n||tTarget==n
tS1=sum(obj.AdjMat(tSource,1:(tTarget-1)))+obj.LayerPos(tSource,3);
tS2=sum(obj.AdjMat(tSource,1:tTarget))+obj.LayerPos(tSource,3);
tT1=sum(obj.AdjMat(1:(tSource-1),tTarget))+obj.LayerPos(tTarget,3);
tT2=sum(obj.AdjMat(1:tSource,tTarget))+obj.LayerPos(tTarget,3);
if isempty(tS1),tS1=0;end
if isempty(tT1),tT1=0;end
tX=[obj.LayerPos(tSource,1),obj.LayerPos(tSource,2),obj.LayerPos(tTarget,1),obj.LayerPos(tTarget,2)];
qX=linspace(obj.LayerPos(tSource,1),obj.LayerPos(tTarget,2),200);qT=linspace(0,1,50);
qY1=interp1(tX,[tS1,tS1,tT1,tT1],qX,'pchip');
qY2=interp1(tX,[tS2,tS2,tT2,tT2],qX,'pchip');
YY=qY1.*(qT'.*0+1)+(qY2-qY1).*(qT');
set(obj.LinkHdl(i),'YData',YY);
end
end
end
end
% Copyright (c) 2023, Zhaoxu Liu / slandarer
% =========================================================================
% @author : slandarer
% 公众号 : slandarer随笔
% 知乎 : slandarer
% -------------------------------------------------------------------------
% Zhaoxu Liu / slandarer (2023). sankey plot
% (https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot),
% MATLAB Central File Exchange. 检索来源 2023/4/28.
end
over
It is really not easy to write this code and select a case. I hope everyone should like it 点赞
and watch it! !
Please do not use this code for commercial purposes without permission. If you quote it, you can quote the link on my file exchange. You can use the following format:
Zhaoxu Liu / slandarer (2023). sankey plot ( https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot ), MATLAB Central File Exchange. Retrieved from 2023/4/28.
If you reprint, please keep the above file exchange link and this article link! ! ! ! !
The tool can be obtained through the fileexchange link above, or downloaded through the following gitee repository: