Hey, it’s been a long time since I last saw you. I finally have some free time to write some articles. I was so busy during this period that I didn’t even have time to read the replies in the background. I’m sorry that some of the comments in the background have exceeded the time limit as soon as I saw them. However, according to everyone’s comments, the demand is mainly for a tutorial on the rising sun chart, and it’s here today~
The sunburst chart is neither simple nor difficult. The only difficulty is probably to classify the data and sort it so that each circle can correspond to it. Coloring and other things are not difficult, so this issue I will simply teach you how to draw a large frame and provide two methods of coloring.
It’s quite cool. I don’t have much time to develop a class that supports more coloring modes in the near future. You can DIY it first based on the code provided in this article.
Tutorial section
0 data
I am too lazy to collect data here, so I randomly generated a table type data. The first three columns are names, and the last column is numerical values:
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);
It probably looks like this:
1 Data processing
Use grp2idx for classification and sortrows to group the same categories together
% Use grp2idx for classification and sortrows to group the same categories together. 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 Attribute default
Set color matching, fonts, text display thresholds and other information:
% Here you can set the color matching 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]; % You can modify the font here FontProp = {<!-- -->'FontSize',12,'Color',[0,0,0]}; % Here you can set the proportion below which text will not be displayed. TextThreshold = 0.012;
3 Drawing
Radial Gradient
% Start drawing 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]; ifi==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 & amp; & amp;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 % Draw the outermost circle pie chart 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 & amp; & amp;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
Tangential gradient
% Start drawing 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]; ifi==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 & amp; & amp;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 % Draw the outermost circle pie chart 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 & amp; & amp;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
Complete code (radial)
Just copy the previous code and replace it with your own data and colors:
Due to limited space, I will only give the complete code for the radial gradient first, and the code for the tangential gradient will just replace the last paragraph slightly:
% @author:slandarer %% ================================================ ======================== % Randomly generate a set of data, and the Table can be replaced by itself 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); %% ================================================ ======================== % Use grp2idx for classification and sortrows to group the same categories together 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); %% ================================================ ======================== % Color matching can be set here 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]; % You can modify the font here FontProp = {'FontSize',12,'Color',[0,0,0]}; % Here you can set the proportion below which text will not be displayed. TextThreshold = 0.012; %% ================================================ ======================== % Start drawing 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]; ifi==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 & amp; & amp;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 % Draw the outermost circle pie chart 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 & amp; & amp;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
End
The above is the complete code. If the code is updated in the future, you can view it in the following gitee repository:
https://gitee.com/slandarer/spdraw/