MATLAB | 两种上色方式的旭日图绘制

文摘   其他   2023-10-25 08:00   山东  
请尊重原创劳动成果
转载请注明本文链接
及文章作者:slandarer

嘿,这次真的是好久不见了,好不容易才有点空写点文章,这段时间忙到后台回复都有点来不及看,很抱歉有一部分后台留言刚看到就已经超过时限没法回复了,不过根据大家的留言,需求主要集中在希望出一期旭日图的教程,今天它来啦~

旭日图要说简单也不简单,要说难也不算难,唯一难点估计就是将数据分类排好序,然后每一圈能对应起来呗,上色啥的也不算啥难点,因此这期就简单教大家如何把大框架画出来以及提供两种上色方式。

还是挺酷的,近期没啥空单独开发出一个支持更多上色模式的类,大家可以先根据本文提供的代码先自行DIY着。


教程部分

0 数据

这里懒得搜集数据,自己随机生成了一个table类型数据,前三列都是名称,最后一列是数值:

rng(1)
ULList = 'ABCD';
LLList = 'abcd';

rowNum = 50;
idx1   = randi([1,length(ULList)],[1,rowNum]);
idx2   = randi([1,3],[rowNum,1]);
Name1  = ULList(idx1'*ones(1,4));
Name2  = [LLList(idx1'*ones(1,4)),num2str(idx2)];
Name3  = [LLList(idx1)',num2str(idx2),...
         char(45.*ones(rowNum,1)),char(randi([97,122],[rowNum,5]))];
Value  = rand([rowNum,1]);
Table  = table(Name1,Name2,Name3,Value);

大概长这样:


1 数据处理

利用 grp2idx 进行分类,利用 sortrows 将相同类归在一起

% 利用 grp2idx 进行分类,利用 sortrows 将相同类归在一起
NameList  = Table.Properties.VariableNames;
NameNum   = length(NameList)-1;
NameCell{NameNum} = ' ';
valueList = zeros(length(Table.(NameList{1})),NameNum);
for i = 1:NameNum-1
    tName = Table.(NameList{i});
    tUniq = unique(tName,'rows');
    NameCell{i} = tUniq;
    ind = grp2idx([tUniq;tName]);
    ind(1:length(tUniq)) = [];
    valueList(:,i) = ind;
end
valueList(:,end) = -Table.(NameList{end});
[VAL,IDX] = sortrows(valueList,1:size(valueList,2));
VAL(:,end) = -VAL(:,end);

2 属性预设

设置配色,字体,文本显示的阈值等信息:

% 此处可以设置配色
CList=[0.3882    0.5333    0.7059
    1.0000    0.6824    0.2039
    0.9373    0.4353    0.4157
    0.5490    0.7608    0.7922
    0.3333    0.6784    0.5373
    0.7647    0.7373    0.2471
    0.7333    0.4627    0.5765
    0.7294    0.6275    0.5804
    0.6627    0.7098    0.6824
    0.4627    0.4627    0.4627];
% 在这可修改字体
FontProp = {'FontSize',12,'Color',[0,0,0]};
% 在这可设置比例低于多少的部分不显示文字
TextThreshold = 0.012;

3 绘图

径向渐变

% 开始绘图
figure('Units','normalized','Position',[.2,.1,.52,.72]);
ax = gca; hold on
ax.DataAspectRatio = [1,1,1];
ax.XColor = 'none';
ax.YColor = 'none';
tT = linspace(0,1,100);
for i = 1:size(VAL,2)-1
    tRateSum = 0;
    tNum = length(NameCell{i});
    for j = 1:tNum
        tRate = sum(VAL(VAL(:,i) == j,end))./sum(VAL(:,end));  
        tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
        tR = [tT.*0+i,tT.*0+i+1];
        if i == 1
            fill(cos(tTheta).*tR,sin(tTheta).*tR,CList(j,:),'EdgeColor',[1,1,1],'LineWidth',1)
        else
            tCN = VAL(find(VAL(:,i) == j,1),1);
            fill(cos(tTheta).*tR,sin(tTheta).*tR,...
                CList(tCN,:).*0.8^(i-1)+[1,1,1].*(1-0.8^(i-1)),'EdgeColor',[1,1,1],'LineWidth',1)
        end
        
        rotation = (tRateSum+tRate/2)*360;
        if tRate > TextThreshold
        if rotation>90&&rotation<270
            rotation=rotation+180;
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i,NameCell{i}(j,:)+" ",FontProp{:},...
                'Rotation',rotation,'HorizontalAlignment','right')
        else
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i," "+NameCell{i}(j,:),FontProp{:},...
                'Rotation',rotation)
        end
        end
        tRateSum = tRateSum+tRate;
    end
end
% 绘制最外圈饼状图
tRateSum = 0;
tNameCell = Table.(NameList{end-1});
for j = 1:size(VAL,1)
    tRate = VAL(j,end)./sum(VAL(:,end)); 
    tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
    tR = [tT.*0+size(VAL,2),tT.*0+size(VAL,2)+1];
    tCN = VAL(j,1);
    fill(cos(tTheta).*tR,sin(tTheta).*tR,CList(tCN,:).*0.8^(size(VAL,2)-1)+...
        [1,1,1].*(1-0.8^(size(VAL,2)-1)),'EdgeColor',[1,1,1],'LineWidth',1)
    rotation = (tRateSum+tRate/2)*360;
    if tRate > TextThreshold
    if rotation>90&&rotation<270
        rotation=rotation+180;
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2),tNameCell(IDX(j),:)+" ",FontProp{:},...
            'Rotation',rotation,'HorizontalAlignment','right')
    else
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2)," "+tNameCell(IDX(j),:),FontProp{:},...
            'Rotation',rotation)
    end
    end
    tRateSum = tRateSum+tRate;
end

切向渐变

% 开始绘图
figure('Units','normalized','Position',[.2,.1,.52,.72]);
ax = gca; hold on
ax.DataAspectRatio = [1,1,1];
ax.XColor = 'none';
ax.YColor = 'none';
tT = linspace(0,1,100);
LCList = CList(1:length(NameCell{1}),:);
for i = 1:size(VAL,2)-1
    tRateSum = 0;
    tNum = length(NameCell{i});
    NCList = zeros(tNum,3);
    for j = 1:tNum
        tRate = sum(VAL(VAL(:,i) == j,end))./sum(VAL(:,end));  
        tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
        tR = [tT.*0+i,tT.*0+i+1];
        if i == 1
            fill(cos(tTheta).*tR,sin(tTheta).*tR,CList(j,:),'EdgeColor',[1,1,1],'LineWidth',1)
        else
            tCN = VAL(find(VAL(:,i) == j,1),i-1);
            tNN = j-VAL(find(VAL(:,i-1) == tCN,1),i)+1;
            tPN = length(unique(VAL(VAL(:,i-1) == tCN,i)));
            if mod(i,2)~=0
                tRN = tNN;
            else
                tRN = tPN+1-tNN;
            end    
            NCList(j,:)=LCList(tCN,:).*0.8^(tRN-1)+[1,1,1].*(1-0.8^(tRN-1));
            fill(cos(tTheta).*tR,sin(tTheta).*tR,NCList(j,:),'EdgeColor',[1,1,1],'LineWidth',1)
        end
        
        rotation = (tRateSum+tRate/2)*360;
        if tRate > TextThreshold
        if rotation>90&&rotation<270
            rotation=rotation+180;
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i,NameCell{i}(j,:)+" ",FontProp{:},...
                'Rotation',rotation,'HorizontalAlignment','right')
        else
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i," "+NameCell{i}(j,:),FontProp{:},...
                'Rotation',rotation)
        end
        end
        tRateSum = tRateSum+tRate;
    end
    if i ~=1
        LCList=NCList;
    end
end
% 绘制最外圈饼状图
tRateSum = 0;
tNameCell = Table.(NameList{end-1});
NCList = zeros(size(VAL,1),3);
for j = 1:size(VAL,1)
    tRate = VAL(j,end)./sum(VAL(:,end)); 
    tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
    tR = [tT.*0+size(VAL,2),tT.*0+size(VAL,2)+1];

    tCN = VAL(j,size(VAL,2)-1);
    tNN = j-find(VAL(:,size(VAL,2)-1) == tCN,1)+1;
    tPN = sum(VAL(:,size(VAL,2)-1) == tCN);
    if mod(size(VAL,2),2)~=0
        tRN = tNN;
    else
        tRN = tPN+1-tNN;
    end
    NCList(j,:)=LCList(tCN,:).*0.8^(tRN-1)+[1,1,1].*(1-0.8^(tRN-1));
    fill(cos(tTheta).*tR,sin(tTheta).*tR,NCList(j,:),'EdgeColor',[1,1,1],'LineWidth',1)
    rotation = (tRateSum+tRate/2)*360;
    if tRate > TextThreshold
    if rotation>90&&rotation<270
        rotation=rotation+180;
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2),tNameCell(IDX(j),:)+" ",FontProp{:},...
            'Rotation',rotation,'HorizontalAlignment','right')
    else
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2)," "+tNameCell(IDX(j),:),FontProp{:},...
            'Rotation',rotation)
    end
    end
    tRateSum = tRateSum+tRate;
end

完整代码(径向)

就是前面的代码顺着复制下来,可以换成自己的数据和颜色: 篇幅有限就只先给出径向渐变完整代码,切向渐变代码就光把最后一段稍微一更换即可:

% @author:slandarer

%% ========================================================================
% 随机生成一组数据,可将 Table 自行更换
rng(1)
ULList = 'ABCD';
LLList = 'abcd';

rowNum = 50;
idx1   = randi([1,length(ULList)],[1,rowNum]);
idx2   = randi([1,3],[rowNum,1]);
Name1  = ULList(idx1'*ones(1,4));
Name2  = [LLList(idx1'*ones(1,4)),num2str(idx2)];
Name3  = [LLList(idx1)',num2str(idx2),...
         char(45.*ones(rowNum,1)),char(randi([97,122],[rowNum,5]))];
Value  = rand([rowNum,1]);
Table  = table(Name1,Name2,Name3,Value);

%% ========================================================================
% 利用 grp2idx 进行分类,利用 sortrows 将相同类归在一起
NameList  = Table.Properties.VariableNames;
NameNum   = length(NameList)-1;
NameCell{NameNum} = ' ';
valueList = zeros(length(Table.(NameList{1})),NameNum);
for i = 1:NameNum-1
    tName = Table.(NameList{i});
    tUniq = unique(tName,'rows');
    NameCell{i} = tUniq;
    ind = grp2idx([tUniq;tName]);
    ind(1:length(tUniq)) = [];
    valueList(:,i) = ind;
end
valueList(:,end) = -Table.(NameList{end});
[VAL,IDX] = sortrows(valueList,1:size(valueList,2));
VAL(:,end) = -VAL(:,end);
%% ========================================================================
% 此处可以设置配色
CList=[0.3882    0.5333    0.7059
    1.0000    0.6824    0.2039
    0.9373    0.4353    0.4157
    0.5490    0.7608    0.7922
    0.3333    0.6784    0.5373
    0.7647    0.7373    0.2471
    0.7333    0.4627    0.5765
    0.7294    0.6275    0.5804
    0.6627    0.7098    0.6824
    0.4627    0.4627    0.4627];
% 在这可修改字体
FontProp = {'FontSize',12,'Color',[0,0,0]};
% 在这可设置比例低于多少的部分不显示文字
TextThreshold = 0.012;
%% ========================================================================
% 开始绘图
figure('Units','normalized','Position',[.2,.1,.52,.72]);
ax = gca; hold on
ax.DataAspectRatio = [1,1,1];
ax.XColor = 'none';
ax.YColor = 'none';
tT = linspace(0,1,100);
for i = 1:size(VAL,2)-1
    tRateSum = 0;
    tNum = length(NameCell{i});
    for j = 1:tNum
        tRate = sum(VAL(VAL(:,i) == j,end))./sum(VAL(:,end));  
        tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
        tR = [tT.*0+i,tT.*0+i+1];
        if i == 1
            fill(cos(tTheta).*tR,sin(tTheta).*tR,CList(j,:),'EdgeColor',[1,1,1],'LineWidth',1)
        else
            tCN = VAL(find(VAL(:,i) == j,1),1);
            fill(cos(tTheta).*tR,sin(tTheta).*tR,...
                CList(tCN,:).*0.8^(i-1)+[1,1,1].*(1-0.8^(i-1)),'EdgeColor',[1,1,1],'LineWidth',1)
        end
        
        rotation = (tRateSum+tRate/2)*360;
        if tRate > TextThreshold
        if rotation>90&&rotation<270
            rotation=rotation+180;
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i,NameCell{i}(j,:)+" ",FontProp{:},...
                'Rotation',rotation,'HorizontalAlignment','right')
        else
            text(cos((tRateSum+tRate/2).*pi.*2)*i,sin((tRateSum+tRate/2).*pi.*2)*i," "+NameCell{i}(j,:),FontProp{:},...
                'Rotation',rotation)
        end
        end
        tRateSum = tRateSum+tRate;
    end
end
% 绘制最外圈饼状图
tRateSum = 0;
tNameCell = Table.(NameList{end-1});
for j = 1:size(VAL,1)
    tRate = VAL(j,end)./sum(VAL(:,end)); 
    tTheta = [tRateSum+tT.*tRate,tRateSum+tRate-tT.*tRate].*pi.*2;
    tR = [tT.*0+size(VAL,2),tT.*0+size(VAL,2)+1];
    tCN = VAL(j,1);
    fill(cos(tTheta).*tR,sin(tTheta).*tR,CList(tCN,:).*0.8^(size(VAL,2)-1)+...
        [1,1,1].*(1-0.8^(size(VAL,2)-1)),'EdgeColor',[1,1,1],'LineWidth',1)
    rotation = (tRateSum+tRate/2)*360;
    if tRate > TextThreshold
    if rotation>90&&rotation<270
        rotation=rotation+180;
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2),tNameCell(IDX(j),:)+" ",FontProp{:},...
            'Rotation',rotation,'HorizontalAlignment','right')
    else
        text(cos((tRateSum+tRate/2).*pi.*2)*size(VAL,2),sin((tRateSum+tRate/2).*pi.*2)*size(VAL,2)," "+tNameCell(IDX(j),:),FontProp{:},...
            'Rotation',rotation)
    end
    end
    tRateSum = tRateSum+tRate;
end

以上以及是完整代码,若日后代码有更新可在以下gitee仓库查看:

https://gitee.com/slandarer/spdraw/



slandarer随笔
slandarer个人公众号,目前主要更新MATLAB相关内容。
 最新文章