MATLAB | 绘图复刻(十四) | 右侧对齐桑基图,及工具函数SSankey更新

news2024/11/17 9:37:49

hey 真的好久不见了,本期既是一期绘图复刻教程,也是我写的工具函数的版本更新,本期复刻的图片来自《Nature》:

  • Elmarakeby, H.A., Hwang, J., Arafeh, R. et al. Biologically informed deep neural network for prostate cancer discovery. Nature 598, 348–352 (2021). https://doi.org/10.1038/s41586-021-03922-4

绘图复刻效果如下:

本文所用工具函数的基本使用教程可参见以下文章:
https://blog.csdn.net/slandarer/article/details/130430147

该文章仅展示工具函数基本使用方法,本文需要用到文末2.0.0版本工具函数

该文章仅展示工具函数基本使用方法,本文需要用到文末2.0.0版本工具函数



绘图复刻教程部分

0 数据

本文所使用的数据大概长这样,存储在.mat文件中:

load('natureRandData.mat')

该文件依旧可以在文末所示仓库获取,当然这组数据是通过以下方式随机生成的,直接复制以下代码也是可以的:

% rng(5)
% SourceValue=[60,40,30];
% LayerNum=[3,10,10,10,10,10,6,1];
% links{1,3}='';
% for k=1
%     TargetValue=zeros(1,LayerNum(k+1));
%     for i=1:LayerNum(k)
%         tValue=randi([1,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,:)=[];
% tResidual=0;
% for k=2:5
%     [~,tindex]=sort(rand([1,10]));
%     for i=1:10
%         tLen=size(links,1);
%         TargetValue(tindex(i))=SourceValue(i);
%         links{tLen+1,1}=[char(64+k),num2str(i)];
%         links{tLen+1,2}=[char(64+k+1),num2str(tindex(i))];
%         links{tLen+1,3}=SourceValue(i);
%     end
%     for i=1:10
%         tLen=size(links,1);
%         links{tLen+1,1}=['Residual-',char(64+k)];
%         links{tLen+1,2}=[char(64+k+1),num2str(tindex(i))];
%         tValue=rand([1,1])*20./(k.^1);
%         links{tLen+1,3}=tValue;
%         if k==2,tResidual=tResidual+tValue;end
%         if k==5,TargetValue(i)=TargetValue(i)+tValue;end
%     end
%     SourceValue=TargetValue;
% end
% k=1;tValue=randi([1,13],[1,3]);
% for i=1:3
%     tLen=size(links,1);
%     links{tLen+1,1}=[char(64+k),num2str(i)];
%     links{tLen+1,2}=['Residual-',char(64+k+1)];
%     links{tLen+1,3}=tValue(i)./sum(tValue).*tResidual;
% end
% for k=6
%     TargetValue=zeros(1,LayerNum(k+1));
%     for i=1:LayerNum(k)
%         tValue=randi([1,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
% k=7;
% for i=1:LayerNum(end-1)
%     tLen=size(links,1);
%     links{tLen+1,1}=[char(64+k),num2str(i)];
%     links{tLen+1,2}=' ';
%     links{tLen+1,3}=SourceValue(i);
% end
% save natureRandData.mat links

1 配色

配色数据就是随便找个颜色提取器提取的论文原图:

CList1=[233,163,117; 150,209,224; 78 ,115,180]./255;
CList2=[149, 77, 85; 182, 85, 90; 208, 91, 94;
        208, 91, 94; 245,124,112; 252,150,128;
        252,173,151; 253,196,176; 253,217,203;
        254,236,228; 230,230,230]./255;

2 基础绘图

% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));

% % 类似于向右侧对齐
% SK.LayerOrder='reverse';

% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='left';  

% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='center';

% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='right';

% 设置缝隙占比(Separation distance proportion)
SK.Sep=0.02;
% SK.Sep=[.08,.04,.02,.05,.05,.06,.07];

% 设置方块占比(Set the scale of blocks)
% BlockScale>0 & BlockScale<1
SK.BlockScale=.16;

% 设置颜色(Set color)
SK.ColorList=[
    CList1;
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:10,:)
    CList2(1:6,:)
    CList2(1,:)
    ];

% 开始绘图(Start drawing)
SK.draw()


3 右侧对齐

我们发现此时有的Residual方块由于左侧没有方块会被排列在最左侧,这样会比较混乱,此时选择将LayerOrder设置为反方向(此操作要在SK.draw()之前):

% 类似于向右侧对齐
SK.LayerOrder='reverse';


4 自定义缝隙比例

我们发现由于流入流出数据不相等,越往右侧整体会越窄,而原图基本上是两端对齐的,因此我们可以调整每一层的缝隙占比,比如(此操作要在SK.draw()之前):

% 设置缝隙占比(Separation distance proportion)
% SK.Sep=0.02;
SK.Sep=[.08,.04,.02,.05,.05,.06,.07];


5 节点方块修饰

加个白框:

for i=1:SK.BN
    SK.setBlock(i,'EdgeColor',[1,1,1],'LineWidth',1)
end


6 添加标签

就直接用text硬画,第i层的横坐标就是i,而宽度是SK.BlockScale,因此比较容易找到每层的X方向中点:

FontCell={'FontSize',22,'FontName','Times New Roman','HorizontalAlignment','center','VerticalAlignment','bottom'};
for i=2:SK.LN-1
    text(i+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),['H',num2str(i-1)],FontCell{:})
end
text(1+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),'Input',FontCell{:})
text(8+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),'OutCome',FontCell{:})


绘图复刻完整代码

% -----------------------------------------------------+
% @author  | slandarer                                 |
% -----------------------------------------------------+
% 复刻自   | www.nature.com/articles/s41586-021-03922-4 |
% -----------------------------------------------------+

figure('Name','nature sankey demo','Units','normalized','Position',[.05,.2,.9,.56])
clc;clear;

%% 配色 ===================================================================
CList1=[233,163,117; 150,209,224; 78 ,115,180]./255;
CList2=[149, 77, 85; 182, 85, 90; 208, 91, 94;
        208, 91, 94; 245,124,112; 252,150,128;
        252,173,151; 253,196,176; 253,217,203;
        254,236,228; 230,230,230]./255;
%% 随意构造数据 ============================================================
load('natureRandData.mat')

%% 绘图主要代码 ============================================================
% 创建桑基图对象(Create a Sankey diagram object)
SK=SSankey(links(:,1),links(:,2),links(:,3));

% 类似于向右侧对齐
SK.LayerOrder='reverse';

% 修改链接颜色渲染方式(Set link color rendering method)
% 'left'/'right'/'interp'(default)/'map'/'simple'
SK.RenderingMethod='left';  

% 修改对齐方式(Set alignment)
% 'up'/'down'/'center'(default)
SK.Align='center';

% 修改文本位置(Set Text Location)
% 'left'(default)/'right'/'top'/'center'/'bottom'
SK.LabelLocation='right';

% 设置缝隙占比(Separation distance proportion)
% SK.Sep=0.02;
SK.Sep=[.08,.04,.02,.05,.05,.06,.07];

% 设置方块占比(Set the scale of blocks)
% BlockScale>0 & BlockScale<1
SK.BlockScale=.16;

% 设置颜色(Set color)
SK.ColorList=[
    CList1;
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:11,:)
    CList2(1:10,:)
    CList2(1:6,:)
    CList2(1,:)
    ];

% 开始绘图(Start drawing)
SK.draw()
%% 修饰 ===================================================================

for i=1:SK.BN
    SK.setBlock(i,'EdgeColor',[1,1,1],'LineWidth',1)
end
FontCell={'FontSize',22,'FontName','Times New Roman','HorizontalAlignment','center','VerticalAlignment','bottom'};
for i=2:SK.LN-1
    text(i+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),['H',num2str(i-1)],FontCell{:})
end
text(1+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),'Input',FontCell{:})
text(8+SK.BlockScale/2,min(min(SK.LayerPos(:,3:4))),'OutCome',FontCell{:})

工具函数完整代码

classdef SSankey < handle
% Copyright (c) 2023, Zhaoxu Liu / slandarer
% =========================================================================
% @author : slandarer
% =========================================================================
% # update 2.0.0(2024-02-04)
% see natureSankeyDemo1.m
%
% + 层向右对齐(Align layers to the right)
%   try : obj.LayerOrder='reverse';
%
% + 单独调整每层间隙大小(Adjust the Sep size of each layer separately)
%   try : obj..Sep=[.2,.06,.05,.07,.07,.08,.15];
% 
    properties
        Source;Target;Value;
        SourceInd;TargetInd;
        Layer;LayerPos;LayerOrder='normal';
        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
% 绘图函数 =================================================================
        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;
            % 计算每个对象位于的层、每层方块长度、每个方块位置 ----------------
            if strcmp(obj.LayerOrder,'normal')
                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
            else
                obj.Layer=zeros(obj.BN,1);
                obj.Layer(sum(obj.BoolMat,2)==0)=-1;
                startMat=diag(obj.Layer);
                for i=1:(obj.BN-1)
                    tLayer=(sum(startMat*(obj.BoolMat.')^i,1)<0).*(-i-1);
                    obj.Layer=min([obj.Layer,tLayer'],[],2);
                end
                obj.Layer=obj.Layer-min(obj.Layer)+1;
            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(min(i,length(obj.Sep)));
                tY2=tBlockLen(2:end)+(0:length(tBlockInd)-1).'.*obj.SepLen(min(i,length(obj.Sep)));
                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
% =========================================================================
        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
% -------------------------------------------------------------------------
end

以上已经是完整代码,对于工具函数2.0.0更新版,未经允许本代码请勿作商业用途,引用的话可以引用我file exchange上的链接,可使用如下格式:

Zhaoxu Liu / slandarer (2023). sankey plot (https://www.mathworks.com/matlabcentral/fileexchange/128679-sankey-plot), MATLAB Central File Exchange. 检索来源 2023/4/28.

若转载请保留以上file exchange链接及本文链接!!!!!

该工具可通过上述fileexchange链接获取,或者通过以下gitee仓库下载:

https://gitee.com/slandarer/matlab-sankey-diagram


对于绘图复刻以及.mat文件,可以去以下gitee仓库获取全部代码:

https://gitee.com/slandarer/PLTreprint/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1435664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++学习Day04之this指针

目录 一、程序及输出1.1 基础使用1.2 *this和链式编程1.2.1 返回引用进行链式编程1.2.2 返回值进行链式编程1.3 注意事项 二、分析与总结 一、程序及输出 在 C 中使用类的成员函数时&#xff0c;可以使用 this 指针来引用当前对象的地址。this 指针是一个隐式参数&#xff0c;它…

计算机视觉-PCV包、Vlfeat库、Graphviz库的下载安装配置及问题解决(使用anaconda3 python 3.8.5)

目录 一、PCV包配置 二、Vlfeat配置 三、在PCV包的sift.py文件中对路径进行修改 四、以上步骤所需注意的错误 五、Graphviz配置 一、PCV包配置 1.下载PCV包,点开网址直接下载安装包(不用解压),下载之后将安装包放在任意目录位置https://codeload.github.com/Li-Shu14…

JavaEE作业-实验一

目录 1 实验内容 2 思路 3 核心代码 &#xff08;1&#xff09;前端核心代码&#xff1a; &#xff08;2&#xff09;后端核心代码&#xff1a; 4 实验结果 1 实验内容 用Servlet JSP JavaBean实现登录功能 2 思路 ①建好web项目,创建数据库 ②建立两个简单的前端页…

【为什么多态中父类要提供虚析构函数?】

为什么多态中父类要提供虚析构函数&#xff1f; 不提供虚析构函数提供虚析构函数 如果说类与类之间产生了继承关系&#xff0c;并且在子类中重写了父类的虚函数&#xff0c;相当于最终要实现多态&#xff0c;就是用父类指针或引用指向子类的对象&#xff0c;并且通过父类指针调…

算法学习——LeetCode力扣链表篇1

算法学习——LeetCode力扣链表篇1 203. 移除链表元素 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 示例 …

爬虫实战--人民网

文章目录 前言发现宝藏 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚&#xff0c;文章中如果有记录错误&#xff0c;欢迎读者朋友们…

【C语言不能不会的操作】调试-万字详解【windows操作系统下】(会写bug还会调试解决bug的程序员简直帅呆了,赶紧点赞收藏)

目录 1. 什么是bug&#xff1f; 2. 调试是什么&#xff1f;有多重要&#xff1f; 2.1调试是什么 2.2 调试的基本步骤 2.3 Debug和Release的介绍 3. Windows环境调试介绍 3.1 调试环境的准备 3.2 学会快捷键 ​编辑 3.3更多的快捷键 3.4 调试的时候查看程序当前信息…

【lesson41】理解文件系统(2)

文章目录 理解文件系统 理解文件系统 我们之前学过&#xff0c;一个文件可以有多个datablock块&#xff0c;但是如果这个文件太大了怎么办&#xff1f;datablock中&#xff0c;不是所有的datablock只能存文件数据&#xff0c;也可以存其它块的块号。 inode Vs 文件名 找到文件…

MySQL数据库③_MySQL数据类型和测试

目录 1. MySQL数据类型分类 1.1 类型汇总 1.2 整数类型 1.3 浮点数类型和定点数类型 1.4 字符串类型和文本类型 1.5 日期与时间类型 1.6 二进制类型 2. 有代表的类型测试 2.1 tinyint类型 2.2 bit类型 2.3 float类型 2.4 decimal类型 2.5 char和varchar类型 2.6 …

Vue3快速上手(一)使用vite创建项目

一、准备 在此之前&#xff0c;你的电脑&#xff0c;需要安装node.js,我这边v18.19.0 wangdymb 2024code % node -v v18.19.0二、创建 执行npm create vuelatest命令即可使用vite创建vue3项目 有的同学可能卡主不动&#xff0c;可能是npm的registry设置的问题 先看下&#x…

为后端做准备

这里写目录标题 flask 文件上传与接收flask应答&#xff08;接收请求&#xff08;文件、数据&#xff09;flask请求&#xff08;上传文件&#xff09;传递参数和文件 argparse 不从命令行调用参数1、设置default值2、"从命令行传入的参数".split()3、[--input,内容] …

备战蓝桥杯---数据结构与STL应用(进阶2)

本文将主要围绕有关map的今典应用展开&#xff1a; 下面我用图进行分析&#xff1a; 下面为AC代码&#xff1a; #include<bits/stdc.h> using namespace std; struct Point {int x,y;bool operator < (const Point & r) const {return x < r.x || ( x r.x &a…

【Java八股面试系列】JVM-垃圾回收

目录 垃圾回收 堆空间的基本结构 内存分配和回收原则 分代收集机制 Minor GC 流程 空间分配担保 老年代 大对象直接进入老年代 长期存活的对象将进入老年代 GC的区域 对象存活判定算法 引用计数法 可达性分析算法 finalize() 字符串常量判活 类判活 垃圾回收算…

智能优化算法 | Matlab实现合作优化算法(CSA)(内含完整源码)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 智能优化算法 | Matlab实现合作优化算法(CSA)(内含完整源码) 源码设计 clear clc close SearchAgents_no=30; % Number of search agents Max_iteration=1000;

宠物空气净化器哪个品牌质量好?实惠的猫用猫用净化器牌子测评

作为宠物主人&#xff0c;我们深知养宠物的乐趣和责任&#xff0c;但同时也面临着一些挑战&#xff0c;比如宠物脱毛、气味和室内空气质量等问题。正因如此&#xff0c;越来越多的家庭选择宠物空气净化器&#xff0c;为我们营造一个清新、健康的居住环境。 无论我们多么喜欢我…

新零售的升维体验,摸索华为云GaussDB如何实现数据赋能

新零售商业模式 商业模式通常是由客户价值、企业资源和能力、盈利方式三个方面构成。其最主要的用途是为实现客户价值最大化。 商业模式通过把能使企业运行的内外各要素整合起来&#xff0c;从而形成一个完整的、高效率的、具有独特核心竞争力的运行系统&#xff0c;并通过最…

防范恶意勒索攻击!亚信安全发布《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件81起&#xff0c;事件数量有所下降&#xff0c;比上月降低20%。 lockbit3.0仍然是影响最严重的勒索家族&#xff1b;akira和incransom也是两个活动频繁的恶意家族&#xff0c;需要注意防范。 本周alphv勒索组织窃取MBC法律专业公司…

车载测试中:如何处理 bug

一&#xff1a;Jira 提交 bug 包含那些内容 二&#xff1a;如何处理现上 bug 三&#xff1a;车载相关的 bug 如何定位 四&#xff1a;遇到 bug &#xff0c;复现不出来怎么办 五&#xff1a;bug 的处理流程 一&#xff1a;Jira 提交 bug 包含那些内容二&#xff1a;如何处理现上…

C#用Array类的FindAll方法和List<T>类的Add方法按关键词在数组中检索元素并输出

目录 一、使用的方法 1. Array.FindAll(T[], Predicate) 方法 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;示例 2.List类的常用方法 &#xff08;1&#xff09;List.Add(T) 方法 &#xff08;2&#xff09;List.RemoveAt(Int32) 方法 &#xff08;3&…

基于若依的ruoyi-nbcio流程管理系统自定义业务实现一种简单的动态任务标题(续)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…