利用Matlab绘制好看的弦图

2022-10-07,,,

弦图在python中以及r中非常常见,但是matlab中却始终没有相关函数,file exchange中也没有工作做的较为完备的弦图绘制函数(不过现在有了,我已经往上面也传了一份hiahiahia)

仅工具函数主体部分约300行,字符数约8000,能画出与r语言同等质量的弦图实属不易,希望能有个`点赞``!!!

由于工具函数过长,将被放在最后展示,以下将先展示函数用法

封面图

使用教程

1.数据格式

数据要求为全部数值大于等于0的数值矩阵,或者table数组,或者数值矩阵+行列名元胞数组,首先举个数值矩阵的例子:

数值矩阵

datamat=randi([0,5],[5,4]); 

% 绘图
cc=chordchart(datamat);
cc=cc.draw();

这样由于没对各个对象命名,因此会自动命名为rncn

数值矩阵+行列名元胞数组

这是最推荐的一种格式:

datamat=[2 0 1 2 5 1 2;
         3 5 1 4 2 0 1;
         4 0 5 5 2 4 3];
colname={'g1','g2','g3','g4','g5','g6','g7'};
rowname={'s1','s2','s3'};

cc=chordchart(datamat,'rowname',rowname,'colname',colname);
cc=cc.draw();

rowname要和矩阵的行相同大小

colname要和矩阵的列相同大小

对于本列子来说第2行第3列数值是1,就说明有一份能量从s2流向g3,也就在这俩之间需要画单位宽度的弦。

table 数组

需要使用如下格式的table数组:

当然,如果各个行没有命名的话依旧会自动命名的。

2.修饰弦

弦的批量修饰

弦的批量修饰可以使用setchordprop函数,一切patch对象所具有的属性均可以被修饰,举个例子(修饰一下弦的颜色,边缘颜色,边缘线形状等):

cc.setchordprop('edgecolor',[.3,.3,.3],'linestyle','--',...
    'linewidth',.1,'facecolor',[.3,.3,.3])

弦的单独修饰

弦的单独修饰可以使用setchordmn函数,其中m,n值是和原始数值矩阵的行列完全对应的,举个例子(把s2流向g4的弦颜色更改为红色):

cc.setchordmn(2,4,'facecolor',[1,0,0])

弦的颜色映射

使用setchordcolorbymap函数进行颜色映射,

matlab 自带的colormap均可用:

或者也可自行放入一个n×3大小的颜色列表,程序会自动对其进行插值:举个例子:

cc.setchordcolorbymap(copper(100))

3.圆弧状方块修饰

圆弧状方块批量修饰

使用

setsquaret_prop

setsquaref_prop

分别修饰上方方块和下方方块,一切patch对象所具有的属性均可以被修饰,举个例子,上方方块批量修饰(改为黑色):

cc.setsquaret_prop('facecolor',[0,0,0])

圆弧状方块单独修饰

使用

setsquaret_n

setsquaref_n

分别修饰上方方块和下方方块,举个例子,上方第二个方块单独修饰(改为红色):

cc.setsquaret_n(2,'facecolor',[.8,0,0])

4.字体调整

使用setfont函数对字体进行调整,所有text对象具有的属性均可以修饰,举个例子(更改文本的字号、字体和颜色):

cc.setfont('fontsize',25,'fontname','cambria','color',[0,0,.8])

5.显示和隐藏刻度

用法:

cc.tickstate('on')
% cc.tickstate('off')

工具函数完整代码

classdef chordchart
% @author : slandarer
% gzh  : slandarer随笔

% 使用示例:
% =========================================================================
% datamat=[2 0 1 2 5 1 2;
%          3 5 1 4 2 0 1;
%          4 0 5 5 2 4 3];
% colname={'g1','g2','g3','g4','g5','g6','g7'};
% rowname={'s1','s2','s3'};
% 
% cc=chordchart(datamat,'rowname',rowname,'colname',colname);
% cc=cc.draw()

    properties
        ax
        arginlist={'colname','rowname'}
        vermatlab   % matlab 版本: r2021a显示为2021,r2021b显示为2021.5
        chordtable  % table数组
        datamat     % 数值矩阵
        colname={}; % 列名称
        rowname={}; % 行名称
        thetasetf
        thetasett
        % -----------------------------------------------------------
        squarefhdl  % 绘制下方方块的图形对象矩阵
        squarethdl  % 绘制下上方方块的图形对象矩阵
        namefhdl    % 绘制下方文本的图形对象矩阵
        namethdl    % 绘制上方文本的图形对象矩阵
        chordmathdl % 绘制弦的图形对象矩阵
        thetatickfhdl % 刻度句柄
        thetatickthdl % 刻度句柄
        rtickfhdl % 轴线句柄
        rtickthdl % 轴线句柄
    end

    methods
        function obj=chordchart(varargin)
            if isa(varargin{1},'matlab.graphics.axis.axes')
                obj.ax=varargin{1};varargin(1)=[];
            else
                obj.ax=gca;
            end
            
            % 获取版本信息
            tver=version('-release');
            obj.vermatlab=str2double(tver(1:4))+(abs(tver(5))-abs('a'))/2;

            if obj.vermatlab<2017
                hold on
            else
                hold(obj.ax,'on')
            end

            
            obj.datamat=varargin{1};varargin(1)=[];
            if isa(obj.datamat,'table')
            obj.chordtable=obj.datamat;
                if isempty(obj.chordtable.properties.rownames)
                    for i=1:size(obj.chordtable.variables,1)
                        obj.rowname{i}=['r',num2str(i)];
                    end
                end
            else

            % 获取其他数据
            for i=1:(length(varargin)-1)
                tid=ismember(obj.arginlist,varargin{i});
                if any(tid)
                obj.(obj.arginlist{tid})=varargin{i+1};
                end
            end
            tzerocell{1,size(obj.datamat,2)}=zeros(size(obj.datamat,1),1);
            for i=1:size(obj.datamat,2)
                tzerocell{1,i}=zeros(size(obj.datamat,1),1);
            end
            if isempty(obj.colname)
                for i=1:size(obj.datamat,2)
                    obj.colname{i}=['c',num2str(i)];
                end
            end
            if isempty(obj.rowname)
                for i=1:size(obj.datamat,1)
                    obj.rowname{i}=['r',num2str(i)];
                end
            end
            

            % 创建table数组
            obj.chordtable=table(tzerocell{:});
            obj.chordtable.variables=obj.datamat;
            obj.chordtable.properties.variablenames=obj.colname;
            obj.chordtable.properties.rownames=obj.rowname;

            help chordchart
            end
        end

        function obj=draw(obj)
            obj.ax.xlim=[-1.38,1.38];
            obj.ax.ylim=[-1.38,1.38];
            obj.ax.xtick=[];
            obj.ax.ytick=[];
            obj.ax.xcolor='none';
            obj.ax.ycolor='none';
            obj.ax.plotboxaspectratio=[1,1,1];

            % 计算绘图所用数值
            tdmat=obj.chordtable.variables;
            tdfrom=obj.chordtable.properties.rownames;
            tdto=obj.chordtable.properties.variablenames;

            tdmatuni=tdmat-min(min(tdmat));
            tdmatuni=tdmatuni./max(max(tdmatuni));

            sep1=1/20;
            sep2=1/40;

            ratiof=sum(tdmat,2)./sum(tdmat,[1,2]);
            ratiof=[0,ratiof'];
            ratiot=[0,sum(tdmat,1)./sum(tdmat,[1,2])];

            sepnumf=size(tdmat,1);
            sepnumt=size(tdmat,2);

            seplen=pi*(1-2*sep1)*sep2;
            baselenf=(pi*(1-sep2)-(sepnumf-1)*seplen);
            baselent=(pi*(1-sep2)-(sepnumt-1)*seplen);
            tcolor=[61 96 137;76 103 86]./255;
            % 绘制下方方块
            for i=1:sepnumf
                theta1=2*pi-pi*sep1/2-sum(ratiof(1:i))*baselenf-(i-1)*seplen;
                theta2=2*pi-pi*sep1/2-sum(ratiof(1:i+1))*baselenf-(i-1)*seplen;
                theta=linspace(theta1,theta2,100);
                x=cos(theta);y=sin(theta);
                obj.squarefhdl(i)=fill([1.05.*x,1.15.*x(end:-1:1)],[1.05.*y,1.15.*y(end:-1:1)],...
                    tcolor(1,:),'edgecolor','none');
                theta3=(theta1+theta2)/2;
                obj.namefhdl(i)=text(cos(theta3).*1.28,sin(theta3).*1.28,tdfrom{i},'fontsize',12,'fontname','arial',...
                    'horizontalalignment','center','rotation',-(1.5*pi-theta3)./pi.*180);
                obj.rtickfhdl(i)=plot(cos(theta).*1.17,sin(theta).*1.17,'color',[0,0,0],'linewidth',.8,'visible','off');
            end
            % 绘制上方放块
            for j=1:sepnumt
                theta1=pi-pi*sep1/2-sum(ratiot(1:j))*baselent-(j-1)*seplen;
                theta2=pi-pi*sep1/2-sum(ratiot(1:j+1))*baselent-(j-1)*seplen;
                theta=linspace(theta1,theta2,100);
                x=cos(theta);y=sin(theta);
                obj.squarethdl(j)=fill([1.05.*x,1.15.*x(end:-1:1)],[1.05.*y,1.15.*y(end:-1:1)],...
                    tcolor(2,:),'edgecolor','none');
                theta3=(theta1+theta2)/2;
                obj.namethdl(j)=text(cos(theta3).*1.28,sin(theta3).*1.28,tdto{j},'fontsize',12,'fontname','arial',...
                    'horizontalalignment','center','rotation',-(.5*pi-theta3)./pi.*180);
                obj.rtickthdl(j)=plot(cos(theta).*1.17,sin(theta).*1.17,'color',[0,0,0],'linewidth',.8,'visible','off');
            end

            colorfunc=colorfuncfactory(flipud(summer(50)));
            % 绘制弦

            for i=1:sepnumf
                for j=sepnumt:-1:1
                    theta1=2*pi-pi*sep1/2-sum(ratiof(1:i))*baselenf-(i-1)*seplen;
                    theta2=2*pi-pi*sep1/2-sum(ratiof(1:i+1))*baselenf-(i-1)*seplen;
                    theta3=pi-pi*sep1/2-sum(ratiot(1:j))*baselent-(j-1)*seplen;
                    theta4=pi-pi*sep1/2-sum(ratiot(1:j+1))*baselent-(j-1)*seplen;

                    trowv=tdmat(i,:);trowv=[0,trowv(end:-1:1)./sum(trowv)];
                    tcolv=tdmat(:,j)';tcolv=[0,tcolv./sum(tcolv)];       

                    % 贝塞尔曲线断点计算
                    theta5=(theta2-theta1).*sum(trowv(1:(sepnumt+1-j)))+theta1;
                    theta6=(theta2-theta1).*sum(trowv(1:(sepnumt+2-j)))+theta1;
                    theta7=(theta3-theta4).*sum(tcolv(1:i))+theta4;
                    theta8=(theta3-theta4).*sum(tcolv(1:i+1))+theta4;
                    tpnt1=[cos(theta5),sin(theta5)];
                    tpnt2=[cos(theta6),sin(theta6)];
                    tpnt3=[cos(theta7),sin(theta7)];
                    tpnt4=[cos(theta8),sin(theta8)];

                    if j==sepnumt,obj.thetasetf(i,1)=theta5;end
                    obj.thetasetf(i,j+1)=theta6;
                    if i==1,obj.thetasett(1,j)=theta7;end
                    obj.thetasett(i+1,j)=theta8;

                    % 计算曲线
                    tline1=beziercurve([tpnt1;0,0;tpnt3],200);
                    tline2=beziercurve([tpnt2;0,0;tpnt4],200);
                    tline3=[cos(linspace(theta6,theta5,100))',sin(linspace(theta6,theta5,100))'];
                    tline4=[cos(linspace(theta7,theta8,100))',sin(linspace(theta7,theta8,100))'];
                    obj.chordmathdl(i,j)=fill([tline1(:,1);tline4(:,1);tline2(end:-1:1,1);tline3(:,1)],...
                         [tline1(:,2);tline4(:,2);tline2(end:-1:1,2);tline3(:,2)],...
                         colorfunc(tdmatuni(i,j)),'facealpha',.3,'edgecolor','none');
                    if tdmat(i,j)==0
                        set(obj.chordmathdl(i,j),'visible','off')
                    end     
                end

                % 绘制刻度线
                tx=[cos(obj.thetasetf(i,:)).*1.17;cos(obj.thetasetf(i,:)).*1.19;nan.*ones(1,sepnumt+1)];
                ty=[sin(obj.thetasetf(i,:)).*1.17;sin(obj.thetasetf(i,:)).*1.19;nan.*ones(1,sepnumt+1)];
                obj.thetatickfhdl(i)=plot(tx(:),ty(:),'color',[0,0,0],'linewidth',.8,'visible','off');
            end
            for j=1:sepnumt
                tx=[cos(obj.thetasett(:,j)').*1.17;cos(obj.thetasett(:,j)').*1.19;nan.*ones(1,sepnumf+1)];
                ty=[sin(obj.thetasett(:,j)').*1.17;sin(obj.thetasett(:,j)').*1.19;nan.*ones(1,sepnumf+1)];
                obj.thetatickthdl(j)=plot(tx(:),ty(:),'color',[0,0,0],'linewidth',.8,'visible','off');
            end


            % 贝塞尔函数
            function pnts=beziercurve(pnts,n)
                t=linspace(0,1,n);
                p=size(pnts,1)-1;
                coe1=factorial(p)./factorial(0:p)./factorial(p:-1:0);
                coe2=((t).^((0:p)')).*((1-t).^((p:-1:0)'));
                pnts=(pnts'*(coe1'.*coe2))';
            end

            % 渐变色句柄生成函数
            function colorfunc=colorfuncfactory(colorlist)
                x=(0:size(colorlist,1)-1)./(size(colorlist,1)-1);
                y1=colorlist(:,1);y2=colorlist(:,2);y3=colorlist(:,3);
                colorfunc=@(x)[interp1(x,y1,x,'linear')',interp1(x,y2,x,'linear')',interp1(x,y3,x,'linear')'];
            end
        end
        % =================================================================
        % 批量弦属性设置
        function setchordprop(obj,varargin)
            tdmat=obj.chordtable.variables;
            for i=1:size(tdmat,1)
                for j=1:size(tdmat,2)
                    set(obj.chordmathdl(i,j),varargin{:});
                end
            end
        end
        % 单独弦属性设置
        function setchordmn(obj,m,n,varargin)
            set(obj.chordmathdl(m,n),varargin{:});
        end
        % 根据colormap映射颜色
        function setchordcolorbymap(obj,colorlist)
            tdmat=obj.chordtable.variables;
            tdmatuni=tdmat-min(min(tdmat));
            tdmatuni=tdmatuni./max(max(tdmatuni));

            colorfunc=colorfuncfactory(colorlist);
            for i=1:size(tdmat,1)
                for j=1:size(tdmat,2)
                    set(obj.chordmathdl(i,j),'facecolor',colorfunc(tdmatuni(i,j)));
                end
            end
            % 渐变色句柄生成函数
            function colorfunc=colorfuncfactory(colorlist)
                x=(0:size(colorlist,1)-1)./(size(colorlist,1)-1);
                y1=colorlist(:,1);y2=colorlist(:,2);y3=colorlist(:,3);
                colorfunc=@(x)[interp1(x,y1,x,'linear')',interp1(x,y2,x,'linear')',interp1(x,y3,x,'linear')'];
            end
        end


        % -----------------------------------------------------------------
        % 批量上方方块属性设置
        function setsquaret_prop(obj,varargin)
            tdmat=obj.chordtable.variables;
            for j=1:size(tdmat,2)
                set(obj.squarethdl(j),varargin{:});
            end
        end
        % 单独上方方块属性设置
        function setsquaret_n(obj,n,varargin)
            set(obj.squarethdl(n),varargin{:});
        end
        % 批量下方方块属性设置
        function setsquaref_prop(obj,varargin)
            tdmat=obj.chordtable.variables;
            for i=1:size(tdmat,1)
                set(obj.squarefhdl(i),varargin{:});
            end
        end
        % 单独上方方块属性设置
        function setsquaref_n(obj,n,varargin)
            set(obj.squarefhdl(n),varargin{:});
        end


        % -----------------------------------------------------------------
        % 字体设置
        function setfont(obj,varargin)
            tdmat=obj.chordtable.variables;
            for i=1:size(tdmat,1)
                set(obj.namefhdl(i),varargin{:});
            end
            for j=1:size(tdmat,2)
                set(obj.namethdl(j),varargin{:});
            end 
        end


        % -----------------------------------------------------------------
        % 刻度开关
        function tickstate(obj,state)
            tdmat=obj.chordtable.variables;
            for i=1:size(tdmat,1)
                set(obj.thetatickfhdl(i),'visible',state);
                set(obj.rtickfhdl(i),'visible',state);
            end
            for j=1:size(tdmat,2)
                set(obj.thetatickthdl(j),'visible',state);
                set(obj.rtickthdl(j),'visible',state);
            end          
        end
    end
end

封面图绘制代码

封面一

% demo 1
% @author : slandarer
% gzh  : slandarer随笔

datamat=[2 0 1 2 5 1 2;
         3 5 1 4 2 0 1;
         4 0 5 5 2 4 3];
colname={'g1','g2','g3','g4','g5','g6','g7'};
rowname={'s1','s2','s3'};

cc=chordchart(datamat,'rowname',rowname,'colname',colname);
cc=cc.draw();

cc.setfont('fontsize',17,'fontname','cambria')
cc.tickstate('on')

封面二

% demo 2
% @author : slandarer
% gzh : slandarer随笔

datamat=[2 0 1 2 5 1 2;
         3 5 1 4 2 0 1;
         4 0 5 5 2 4 3];
colname={'g1','g2','g3','g4','g5','g6','g7'};
rowname={'s1','s2','s3'};

cc=chordchart(datamat,'rowname',rowname,'colname',colname);
cc=cc.draw();

% 弦属性设置 ===============================================================
% cc.setchordprop('edgecolor',[.3,.3,.3],'linestyle','--',...
%     'linewidth',.1,'facecolor',[.3,.3,.3])

% cc.setchordmn(2,4,'facecolor',[1,0,0])

cc.setchordcolorbymap(copper(100))


% 方块属性设置 =============================================================
cc.setsquaret_prop('facecolor',[0,0,0])
cc.setsquaret_n(2,'facecolor',[.8,0,0])
% cc.setsquaref_prop('facecolor',[0,0,0])
% cc.setsquaref_n(2,'facecolor',[.8,0,0])

% 字体设置 =================================================================
cc.setfont('fontsize',17,'fontname','cambria','color',[0,0,.8])


% 刻度开关设置 =============================================================
cc.tickstate('on')

以上就是利用matlab绘制好看的弦图的详细内容,更多关于matlab弦图的资料请关注其它相关文章!

《利用Matlab绘制好看的弦图.doc》

下载本文的Word格式文档,以方便收藏与打印。