MATLAB | 绘图复刻(十五) | 环形聚类树状图

news/2024/5/20 6:03:11 标签: matlab, 聚类, 开发语言

本期复刻效果:

感觉出的聚类分析树状图绘制工具也不少了,未来可能会统一整理为一个工具包?(任重道远,道阻且长):


代码讲解

0 数据设置

写了比较多的注释应该比较易懂:

matlab">clc; clear; close all

% 样品起名slan1 slan2 slan3...slan75
sampleName = compose('slan%d', 1:75);

% 随机生成数据
% rng(10)
Data = rand(75,3);

% 分类数
N = 5;

% 分类名 Class-A Class-B...
className = compose('Class-%c', 64 + (1:N));

% 设置字体
sampleFont = {'FontSize', 12, 'FontName', 'Times New Roman'};
classFont = {'FontSize', 18, 'FontName', 'Times New Roman', 'FontWeight', 'bold'};

% 设置半径(树状图半径为1)
% 样本文本 类弧形内侧 类弧形外侧 类文本
RSet = [1+1/30, 1.22, 1.27, 1.35]; 

分类数N不宜设置太大,不然会出现比较多一个样本就是一个类的情况:


1 配色

这里使用的是MATLAB自带lines配色:

matlab">% 配色
CList = lines(N);

当然也比较推荐使用我写的slanCL配色包:https://slandarer.blog.csdn.net/article/details/129828666

给几个比较好看的配色:

matlab">% CList = slanCL(251,1:N);
% CList = slanCL(495,1:N);
% CList = slanCL(1838,1:N);
% CList = slanCL(319,1:N);
% CList = slanCL(361,1:N);
% CList = slanCL(455,1:N);

251

495

1838


2 创建绘图图窗

matlab">fig1 = figure('Units', 'normalized', 'Position', [.1,.1,.5,.8], 'Color', 'w');
ax1 = gca;
ax1.NextPlot = 'add';
ax1.DataAspectRatio = [1,1,1];
ax1.XColor = 'none';
ax1.YColor = 'none';
axis tight
fig2 = figure();

3 数据处理、绘制树状图、提取图形、关闭图窗

matlab">Z = linkage(Data,'average');
T = cluster(Z,'maxclust',N);
cutoff = median([Z(end-(N-1),3), Z(end-(N-2),3)]);
[LineSet, ~, order] = dendrogram(Z, 0, 'Orientation', 'top');
XSet = reshape([LineSet(:).XData], 4, []).';
YSet = reshape([LineSet(:).YData], 4, []).';
close(fig2)


4 环形树状图

matlab">% 角度转换数据预处理
TT = T(order);
theta1 = 0;
theta2 = pi*2;
theta3 = (theta2-theta1)./size(Data,1);
theta4 = theta1 + theta3/2;
theta5 = theta2 - theta3/2;
maxY = max(max(YSet));
tS = linspace(0,1,50);

% 绘制环形树状图
tT = theta4 + (theta5-theta4).*(XSet-1)./(size(Data,1)-1);
tR = maxY-YSet;
tR = [tR(:,1), tR(:,2).*ones(1,50), tR(:,4)].';
tT = [tT(:,1), tT(:,2)+tS.*(tT(:,3)-tT(:,2)), tT(:,4)].';
plot(ax1, tR.*cos(tT), tR.*sin(tT), 'Color', 'k', 'LineWidth', .7);


5 添加标签文本

matlab">% 绘制样本名称标签
for i = 1:length(order)
    tT = theta4 + (theta5-theta4).*(i-1)./(size(Data,1)-1);
    if tT<pi/2 || tT>3*pi/2
        text(ax1, maxY.*RSet(1).*cos(tT), maxY.*RSet(1).*sin(tT), sampleName{order(i)},...
        'FontSize', 12, 'Rotation', tT./pi.*180, sampleFont{:});
    else
        text(ax1, maxY.*RSet(1).*cos(tT), maxY.*RSet(1).*sin(tT), sampleName{order(i)},...
        'FontSize', 12, 'Rotation', tT./pi.*180+180, 'HorizontalAlignment', 'right', sampleFont{:});
    end
end


6 绘制聚类信息

matlab">% 绘制分类信息
XSet = [XSet(:,1:2); XSet(:,3:4)];
YSet = [YSet(:,1:2); YSet(:,3:4)];
BSet = (YSet(:,1)-cutoff).*(YSet(:,2)-cutoff)<0;
HSet = (YSet(BSet,1)+YSet(BSet,2))./2;
Cset = TT(round(XSet(BSet,1)));
classNum = unique(TT, 'stable');

for i = 1:length(classNum)
    % 绘制内部分类扇形
    tX = [find(TT==classNum(i),1,'first')-.5, find(TT==classNum(i),1,'last')+.5];
    tR = [maxY-HSet(Cset==classNum(i)), maxY];
    tT = theta4 + (theta5-theta4).*(tX-1)./(size(Data,1)-1);
    tR = [tR(1), tR(2).*ones(1,50), tR(1), tR(1).*ones(1,50)];
    tT = [tT(1), tT(1)+tS.*(tT(2)-tT(1)), tT(2), tT(2)+tS.*(tT(1)-tT(2))];
    patch(ax1, tR.*cos(tT), tR.*sin(tT), CList(i,:), 'EdgeColor', 'none', 'FaceAlpha', .25);

    % 绘制外部分类扇形
    tX = [find(TT==classNum(i),1,'first')-.2, find(TT==classNum(i),1,'last')+.2];
    tR = maxY.*RSet(1,2:3);
    tT = theta4 + (theta5-theta4).*(tX-1)./(size(Data,1)-1);
    tR = [tR(1), tR(2).*ones(1,50), tR(1), tR(1).*ones(1,50)];
    tT = [tT(1), tT(1)+tS.*(tT(2)-tT(1)), tT(2), tT(2)+tS.*(tT(1)-tT(2))];
    patch(ax1, tR.*cos(tT), tR.*sin(tT), CList(i,:), 'EdgeColor', 'none', 'FaceAlpha', .9);

    % 绘制分类信息标签
    tT = mean(tT);
    if tT<pi
        text(ax1, maxY.*RSet(4).*cos(tT), maxY.*RSet(4).*sin(tT), className{i}, 'Color', CList(i,:),...
        'FontSize', 16, 'Rotation', tT./pi.*180-90, 'HorizontalAlignment', 'center', classFont{:});
    else
        text(ax1, maxY.*RSet(4).*cos(tT), maxY.*RSet(4).*sin(tT), className{i}, 'Color', CList(i,:),...
        'FontSize', 16, 'Rotation', tT./pi.*180+180-90, 'HorizontalAlignment', 'center', classFont{:});
    end
end


完整代码

matlab">% clusterTreeDemo
% Copyright (c) 2024, Zhaoxu Liu / slandarer

clc; clear; close all

% 样品起名slan1 slan2 slan3...slan75
sampleName = compose('slan%d', 1:75);

% 随机生成数据
% rng(10)
Data = rand(75,3);

% 分类数
N = 5;

% 分类名 Class-A Class-B...
className = compose('Class-%c', 64 + (1:N));

% 设置字体
sampleFont = {'FontSize', 12, 'FontName', 'Times New Roman'};
classFont = {'FontSize', 18, 'FontName', 'Times New Roman', 'FontWeight', 'bold'};

% 设置半径(树状图半径为1)
% 样本文本 类弧形内侧 类弧形外侧 类文本
RSet = [1+1/30, 1.22, 1.27, 1.35]; 

% 配色
CList = lines(N);
% CList = slanCL(251,1:N);
% CList = slanCL(495,1:N);
% CList = slanCL(1838,1:N);
% CList = slanCL(319,1:N);
% CList = slanCL(361,1:N);
% CList = slanCL(455,1:N);

% =========================================================================
% 绘图部分代码
% -------------------------------------------------------------------------
% 创建绘图图窗
fig1 = figure('Units', 'normalized', 'Position', [.1,.1,.5,.8], 'Color', 'w');
ax1 = gca;
ax1.NextPlot = 'add';
ax1.DataAspectRatio = [1,1,1];
ax1.XColor = 'none';
ax1.YColor = 'none';
axis tight
fig2 = figure();

% 数据处理、绘制树状图、提取图形、关闭图窗
Z = linkage(Data,'average');
T = cluster(Z,'maxclust',N);
cutoff = median([Z(end-(N-1),3), Z(end-(N-2),3)]);
[LineSet, ~, order] = dendrogram(Z, 0, 'Orientation', 'top');
XSet = reshape([LineSet(:).XData], 4, []).';
YSet = reshape([LineSet(:).YData], 4, []).';
close(fig2)

% 角度转换数据预处理
TT = T(order);
theta1 = 0;
theta2 = pi*2;
theta3 = (theta2-theta1)./size(Data,1);
theta4 = theta1 + theta3/2;
theta5 = theta2 - theta3/2;
maxY = max(max(YSet));
tS = linspace(0,1,50);

% 绘制环形树状图
tT = theta4 + (theta5-theta4).*(XSet-1)./(size(Data,1)-1);
tR = maxY-YSet;
tR = [tR(:,1), tR(:,2).*ones(1,50), tR(:,4)].';
tT = [tT(:,1), tT(:,2)+tS.*(tT(:,3)-tT(:,2)), tT(:,4)].';
plot(ax1, tR.*cos(tT), tR.*sin(tT), 'Color', 'k', 'LineWidth', .7);

% 绘制样本名称标签
for i = 1:length(order)
    tT = theta4 + (theta5-theta4).*(i-1)./(size(Data,1)-1);
    if tT<pi/2 || tT>3*pi/2
        text(ax1, maxY.*RSet(1).*cos(tT), maxY.*RSet(1).*sin(tT), sampleName{order(i)},...
        'FontSize', 12, 'Rotation', tT./pi.*180, sampleFont{:});
    else
        text(ax1, maxY.*RSet(1).*cos(tT), maxY.*RSet(1).*sin(tT), sampleName{order(i)},...
        'FontSize', 12, 'Rotation', tT./pi.*180+180, 'HorizontalAlignment', 'right', sampleFont{:});
    end
end

% 绘制分类信息
XSet = [XSet(:,1:2); XSet(:,3:4)];
YSet = [YSet(:,1:2); YSet(:,3:4)];
BSet = (YSet(:,1)-cutoff).*(YSet(:,2)-cutoff)<0;
HSet = (YSet(BSet,1)+YSet(BSet,2))./2;
Cset = TT(round(XSet(BSet,1)));
classNum = unique(TT, 'stable');

for i = 1:length(classNum)
    % 绘制内部分类扇形
    tX = [find(TT==classNum(i),1,'first')-.5, find(TT==classNum(i),1,'last')+.5];
    tR = [maxY-HSet(Cset==classNum(i)), maxY];
    tT = theta4 + (theta5-theta4).*(tX-1)./(size(Data,1)-1);
    tR = [tR(1), tR(2).*ones(1,50), tR(1), tR(1).*ones(1,50)];
    tT = [tT(1), tT(1)+tS.*(tT(2)-tT(1)), tT(2), tT(2)+tS.*(tT(1)-tT(2))];
    patch(ax1, tR.*cos(tT), tR.*sin(tT), CList(i,:), 'EdgeColor', 'none', 'FaceAlpha', .25);

    % 绘制外部分类扇形
    tX = [find(TT==classNum(i),1,'first')-.2, find(TT==classNum(i),1,'last')+.2];
    tR = maxY.*RSet(1,2:3);
    tT = theta4 + (theta5-theta4).*(tX-1)./(size(Data,1)-1);
    tR = [tR(1), tR(2).*ones(1,50), tR(1), tR(1).*ones(1,50)];
    tT = [tT(1), tT(1)+tS.*(tT(2)-tT(1)), tT(2), tT(2)+tS.*(tT(1)-tT(2))];
    patch(ax1, tR.*cos(tT), tR.*sin(tT), CList(i,:), 'EdgeColor', 'none', 'FaceAlpha', .9);

    % 绘制分类信息标签
    tT = mean(tT);
    if tT<pi
        text(ax1, maxY.*RSet(4).*cos(tT), maxY.*RSet(4).*sin(tT), className{i}, 'Color', CList(i,:),...
        'FontSize', 16, 'Rotation', tT./pi.*180-90, 'HorizontalAlignment', 'center', classFont{:});
    else
        text(ax1, maxY.*RSet(4).*cos(tT), maxY.*RSet(4).*sin(tT), className{i}, 'Color', CList(i,:),...
        'FontSize', 16, 'Rotation', tT./pi.*180+180-90, 'HorizontalAlignment', 'center', classFont{:});
    end
end

以上已经是本文全部内容,若懒得一一获取代码,可以去以下gitee仓库获取全部代码:

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


http://www.niftyadmin.cn/n/5381253.html

相关文章

springboot拦截器配置

1、首先我们需要创建interceptor并实现springMVC中的HandlerInterceptor package com.example.cybg.web.intercepter;import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServl…

数据结构-邻接矩阵的创建与遍历

上篇文章已经介绍了邻接矩阵的具体作用与如果利用邻接矩阵寻找相邻顶点&#xff0c;这次介绍重点为邻接矩阵的创建与两种遍历方式 邻接矩阵的创建 其结构体需要能记录顶点、顶点数、边数及邻接矩阵&#xff0c;即 #define max 100 typedef struct {int vex[max];//顶点(假设…

java之VO,BO,PO,DO,DTO

概念 VO&#xff08;View Object&#xff09;&#xff1a;视图对象&#xff0c;用于展示层&#xff0c;它的作用是把某个指定页面&#xff08;或组件&#xff09;的所有数据封装起来。DTO&#xff08;Data Transfer Object&#xff09;&#xff1a;数据传输对象&#xff0c;这…

百度智能云分布式数据库 GaiaDB-X 与龙芯平台完成兼容认证

近日&#xff0c;百度智能云的分布式关系型数据库软件 V3.0 与龙芯中科技术股份有限公司的龙芯 3C5000L/3C5000 处理器平台完成兼容性测试&#xff0c;功能与稳定性良好&#xff0c;获得了龙架构兼容互认证证书。 龙芯系列处理器 通用 CPU 处理器是信息产业的基础部件&#xf…

阿里云香港轻量应用服务器怎么样,建站速度快吗?

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…

自动驾驶中的 DCU、MCU、MPU、SOC 和汽车电子架构

自动驾驶中的 DCU、MCU、MPU 1. 分布式电子电气架构2. 域集中电子电气架构架构2.1 通用硬件定义 3. 车辆集中电子电气架构4. ADAS/AD系统方案演变进程梳理4.1 L0-L2级别的ADAS方案4.2 L2以上级别的ADAS方案 5. MCU和MPU区别5.1 MCU和MPU的区别5.2 CPU与SoC的区别5.3 举个例子 R…

【ArcGIS Pro二次开发】(81):文本符号_CIMTextSymbol

CIMTextSymbol是用于绘制文本图形注释的文本符号。 0、属性 Angel文本符号的放置角度BlockProgression多行文本的堆叠方向Callout引线DrawSoftHyphen连字符FlipAngle文本翻转&#xff08;镜像&#xff09;到位的角度FontEffects上下标FontEncoding获取文字的EncodingFontFamil…

从零开发短视频电商 Nginx一个域名部署多个VUE前端

这里介绍的是所有请求不管是前端还是后端都是在一个域名下。 但是 但是 但是&#xff0c;如果你能控制域名的映射强烈建议你使用多个二级域名来实现。 1.打开 Nginx 的配置文件 nginx.conf&#xff0c;该文件通常位于 /etc/nginx/ 或者 /usr/local/nginx/conf/ 中。 2.修改ngi…