MATLAB | 绘图复刻(二十一)| 扇形热图+小提琴图

前段时间在小红书刷到了一个很有特色的热力图,由大佬@滚筒洗衣机创作,感觉很有意思,尝试 MATLAB 复刻:

作者使用的是 python 代码,赶快去瞅瞅。

复刻效果

正文部分

0.数据准备

数据需要一个用来画热图的矩阵以及一个和矩阵相同列数的数组或者元胞数组。例如:

% 例子1 随便构造数据
% 随便构造的数据,可以换成自己的数据
clc; clear; rng(4)
Data = rand([7, 12]) + 1 + sin(linspace(0, 2*pi, 12) - pi/1.2) + (1:7).'./12;
Data = Data./max(max(Data));% 绘制小提琴图的数据,应为列数与 Data 相同的矩阵或元胞数组
VData = mean(Data, 1) + randn([50, size(Data, 2)]).*.6;

这组数据画出来大概长这样:

此外假如我有两行数据,一行是日期,一行是数值,我们也可以直接将这组数据进行处理,统计其均值来画热图,然后按照每个月的数据分类画小提琴图,例如我们让 chatGPT 生成一组数据:

% 对chatGPT:
% 使用matlab构造一组数据,为2018第一天到2024年最后一天的数据,要求具有季节性clc;clear
% 创建日期向量
t = datetime(2018,1,1):days(1):datetime(2024,12,31);
n = length(t);% 将日期转换为一年中的位置(1 到 365/366)
day_of_year = day(t, 'dayofyear');
year_length = year(t);  % 判断闰年时有用
is_leap = eomday(year(t),2) == 29;% 构造季节性数据:例如正弦函数,每年重复一次(周期365)
% 基本模式:sin(2*pi * day_of_year / 365)
% 添加噪声 + 趋势(可选)
seasonal = 10 * sin(2*pi * day_of_year ./ 365);   % 季节性(年周期)
noise = randn(1, n).*5;                           % 噪声
trend = 0.01 * (1:n);                             % 微小上升趋势% 最终数据
v = seasonal + noise + trend;% 可视化
plot(t, v)
xlabel('Date')
ylabel('Value')
title('Synthetic Seasonal Data (2018–2024)')Data.t = t;
Data.v = v;
save test.mat Data

这组数据大概长这样:

我们读取存储的数据并进行统计:

% 例子2 已有各年份每一天数据,对其进行统计
clc; clear
testData = load('test.mat');
t = testData.Data.t;
v = testData.Data.v;
y = 2024:-1:2018;
% 构造一个矩阵,第 i 行第 j 列是第 i 年第 j 个月的数值平均值
Data = zeros(length(y), 12);
for i = 1:length(y)for m = 1:12idx = (year(t) == y(i)) & (month(t) == m);Data(i, m) = mean(v(idx));end
end
% 构造一个元胞数组,第 i 个元胞是 i 月全部数值合集
VData{12} = [];
for m = 1:12idx = (month(t) == m);VData{m} = v(idx)';
end

这组数据画出图来形状如下:

当然还需要定义行标签和列标签:

% 矩阵每行名称
% rowName = {'2024','2023','2022','2021',2020','2019','2018'};
rowName = compose('%d',2024:-1:2018);% 矩阵每列名称
colName = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ...'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'};

1.基础样式

基本不太用动,数据范围及刻度位置不设置的话会自动计算,那个被注释掉的配色就是我尝试仿照原作者弄的配色:

% 标签格式
fontProp = {'FontSize',16, 'FontName','Times New Roman'};% 配色
CMap = 'summer';
% CMap = interp1([0,.5,1], [214,115,144;255,238,234;107,152,191]./255, 0:.01:1);% 小提琴图宽度
width = 0.9;% 数据范围,以及刻度位置
% VLim = [0, 1];
% VTick = 0:.2:1;
VLim = [];
VTick = [];


完整代码

因为绘图部分比较长,我这里直接放一下带着前面数据定义的完整代码得了:

% ----------------------------------------------------------------------
% 例子1 随便构造数据
% 随便构造的数据,可以换成自己的数据
clc; clear; rng(4)
Data = rand([7, 12]) + 1 + sin(linspace(0, 2*pi, 12) - pi/1.2) + (1:7).'./12;
Data = Data./max(max(Data));% 绘制小提琴图的数据,应为列数与 Data 相同的矩阵或元胞数组
VData = mean(Data, 1) + randn([50, size(Data, 2)]).*.6;% ----------------------------------------------------------------------
% % 例子2 已有各年份每一天数据,对其进行统计
% clc; clear
% testData = load('test.mat');
% t = testData.Data.t;
% v = testData.Data.v;
% y = 2024:-1:2018;
% % 构造一个矩阵,第 i 行第 j 列是第 i 年第 j 个月的数值平均值
% Data = zeros(length(y), 12);
% for i = 1:length(y)
%     for m = 1:12
%         idx = (year(t) == y(i)) & (month(t) == m);
%         Data(i, m) = mean(v(idx));
%     end
% end
% % 构造一个元胞数组,第 i 个元胞是 i 月全部数值合集
% VData{12} = [];
% for m = 1:12
%     idx = (month(t) == m);
%     VData{m} = v(idx)';
% end% 矩阵每行名称
% rowName = {'2024','2023','2022','2021',2020','2019','2018'};
rowName = compose('%d',2024:-1:2018);% 矩阵每列名称
colName = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ...'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'};%% 格式设置 ===============================================================
% 标签格式
fontProp = {'FontSize',16, 'FontName','Times New Roman'};% 配色
CMap = 'summer';
% CMap = interp1([0,.5,1], [214,115,144;255,238,234;107,152,191]./255, 0:.01:1);% 小提琴图宽度
width = 0.9;% 数据范围,以及刻度位置
% VLim = [0, 1];
% VTick = 0:.2:1;
VLim = [];
VTick = [];%% 数据预处理 ============================================================
% 如果不设置 VLim, VTick
% 则自动计算一个比较合理的刻度位置
linearTickCompactDegree = 5;
if isempty(VLim)if iscell(VData)VLim = [min(min(min(Data)), min(cellfun(@min, VData)))...max(max(max(Data)), max(cellfun(@max, VData)))];elseVLim = [min(min(min(Data)), min(min(VData)))...max(max(max(Data)), max(max(VData)))];end
end
if isempty(VTick)tXS = diff(VLim) / linearTickCompactDegree;tXN = ceil(log(tXS) / log(10));tXS = round(round(tXS / 10^(tXN-2)) / 5) * 5 * 10^(tXN-2);tVTick1 = 0:-tXS:VLim(1);tVTick2 = 0:tXS:VLim(2);VTick = unique([tVTick1, tVTick2]);VTick(VTick < VLim(1)) = [];VTick(VTick > VLim(2)) = [];
end%% 绘图部分代码 ============================================================
% 构造图窗及坐标区域
fig = figure('Units','normalized', 'Position',[.1,.1,.6,.8], 'Color','w');
ax = axes('Parent',fig, 'XLim',[-sqrt(3), sqrt(3)], 'YLim',[0, 2], ...'DataAspectRatio',[1,1,1], 'Position',[.05,.2,.9,.8], ...'NextPlot','add', 'XColor','none', 'YColor','none');
colormap(CMap)% 绘制射线
w = 1*pi/3/size(Data, 2);
tt = linspace(5*pi/6 - w, pi/6 + w, size(Data, 2));
xx = cos(tt).*2; xx = [xx; xx.*0; xx.*nan(1)];
yy = sin(tt).*2; yy = [yy; yy.*0; yy.*nan(1)];
plot(ax, xx(:), yy(:), 'LineWidth',1, 'Color',[1,1,1].*.8, 'LineStyle','--')% 绘制列标签
for i = 1:length(colName)text(ax, cos(tt(i)).*2.01, sin(tt(i)).*2.01, colName(i), ...'HorizontalAlignment','center', 'VerticalAlignment', 'bottom', ...'Rotation', tt(i)/pi*180 - 90, fontProp{:})
end% 绘制行名称标签
for i = 1:length(rowName)r = 2/5 + (size(Data, 1) - i + .5)*4/size(Data, 1)/5;if mod(i, 2) == 1text(ax, cos(5*pi/6).*r - 1/100, sin(5*pi/6).*r - sqrt(3)/100, rowName{i}, ...'HorizontalAlignment','right', 'Rotation',60, fontProp{:})elsetext(ax, cos(pi/6).*r + 1/100, sin(pi/6).*r - sqrt(3)/100, rowName{i}, ...'HorizontalAlignment','left', 'Rotation',-60, fontProp{:})end
end% 绘制刻度轴线
plot(ax, cos(5*pi/6).*[6/5 + 1/10, 10/5 - 1/10], ...sin(5*pi/6).*[6/5 + 1/10, 10/5 - 1/10], 'LineWidth',1, 'Color','k')
plot(ax, cos(pi/6).*[6/5 + 1/10, 10/5 - 1/10], ...sin(pi/6).*[6/5 + 1/10, 10/5 - 1/10], 'LineWidth',1, 'Color','k')% 绘制刻度和刻度标签
for i = 1:length(VTick)r = (VTick(i) - VLim(1))./diff(VLim).*(3/5) + (6/5 + 1/10);x1 = [cos(5*pi/6).*r, cos(5*pi/6).*r + 1/100];y1 = [sin(5*pi/6).*r, sin(5*pi/6).*r + sqrt(3)/100];plot(ax, x1, y1, 'LineWidth',1, 'Color','k')x2 = [cos(pi/6).*r, cos(pi/6).*r - 1/100];y2 = [sin(pi/6).*r, sin(pi/6).*r + sqrt(3)/100];plot(ax, x2, y2, 'LineWidth',1, 'Color','k')if mod(length(VTick) - i, 2) == 0text(ax, cos(5*pi/6).*r - 1/100, sin(5*pi/6).*r - sqrt(3)/100, num2str(VTick(i)), ...'HorizontalAlignment','right', 'Rotation',60, fontProp{:})elsetext(ax, cos(pi/6).*r + 1/100, sin(pi/6).*r - sqrt(3)/100, num2str(VTick(i)), ...'HorizontalAlignment','left', 'Rotation',-60, fontProp{:})end
end% 绘制小提琴图
maxf = 0;
for i = 1:size(Data, 2)if iscell(VData)tY = VData{i};elsetY = VData(:,i);endtY(isnan(tY)) = [];[f, yi] = ksdensity(tY);maxf = max(maxf, max(f));
end
for i = 1:size(Data, 2)if iscell(VData)tY = VData{i};elsetY = VData(:,i);endtY(isnan(tY)) = [];[f, yi] = ksdensity(tY);yyi = [min(tY), yi(yi<max(tY) & yi>min(tY)), max(tY)];ind1 = find(yi<max(tY) & yi>min(tY), 1, 'first');ind2 = find(yi<max(tY) & yi>min(tY), 1, 'last');f1 = interp1(yi((ind1 - 1):ind1), f((ind1 - 1):ind1), min(tY));f2 = interp1(yi(ind2:(ind2 + 1)), f(ind2:(ind2 + 1)), max(tY));ff = [f1, f(yi<max(tY) & yi>min(tY)), f2];xx = [ff, -ff(end:-1:1)]./maxf.*(4*pi/5/size(Data, 2)).*width./2;yy = ([yyi, yyi(end:-1:1)] - VLim(1))./diff(VLim).*3./5 + 6/5 + 1/10;xy = [cos(tt(i) - pi/2), - sin(tt(i) - pi/2);sin(tt(i) - pi/2), cos(tt(i) - pi/2)]*[xx; yy];% 绘制小提琴fill(ax, xy(1,:), xy(2,:), mean(tY), 'EdgeColor',[0,0,0], 'LineWidth',1)qt25 = quantile(tY, 0.25);qt75 = quantile(tY, 0.75);med = median(tY);ind3 = find(yi < qt25, 1, 'last');ind4 = find(yi < qt75, 1, 'last');ind5 = find(yi < med, 1, 'last');f3 = interp1(yi(ind3:(ind3 + 1)), f(ind3:(ind3 + 1)), qt25);f4 = interp1(yi(ind4:(ind4 + 1)), f(ind4:(ind4 + 1)), qt75);f5 = interp1(yi(ind5:(ind5 + 1)), f(ind5:(ind5 + 1)), med);xx = [f3, -f3, f4, -f4, f5, -f5]./maxf.*(4*pi/5/size(Data, 2)).*width./2;yy = ([qt25, qt25, qt75, qt75, med, med] - VLim(1))./diff(VLim).*3./5 + 6/5 + 1/10;xy = [cos(tt(i) - pi/2), - sin(tt(i) - pi/2);sin(tt(i) - pi/2), cos(tt(i) - pi/2)]*[xx; yy];% 绘制四分位线和中位线plot(ax, xy(1,1:2), xy(2,1:2), 'LineWidth',1, 'Color','k')plot(ax, xy(1,3:4), xy(2,3:4), 'LineWidth',1, 'Color','k')plot(ax, xy(1,5:6), xy(2,5:6), 'LineWidth',2, 'Color','k')
end% 绘制热图
TT = linspace(5*pi/6, pi/6, size(Data, 2) + 1);
for i = 1:size(Data, 1)for j = 1:size(Data, 2)tt = linspace(TT(j), TT(j + 1), 30);r1 = 2/5 + (i - 1)*4/size(Data, 1)/5;r2 = 2/5 + i*4/size(Data, 1)/5;xx = [cos(tt).*r1, cos(tt(end:-1:1)).*r2];yy = [sin(tt).*r1, sin(tt(end:-1:1)).*r2];fill(ax, xx, yy, Data(i,j), 'EdgeColor','w', 'LineWidth',1)end
end% 绘制最上方弧线
tt = linspace(5*pi/6, pi/6, 80);
xx = cos(tt).*2;
yy = sin(tt).*2;
plot(ax, xx(:), yy(:), 'LineWidth',1, 'Color','k')colorbar(ax, 'Position',[.5-.01,.1,.02,.2], fontProp{:});

以上即为完整代码,数据和完整代码已经放在以下gitee仓库:

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

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

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

相关文章

批量PDF转换工具,一键转换Word Excel

软件介绍 今天为大家推荐一款高效的Office文档批量转换工具&#xff0c;能够快速将Word和Excel文件批量转换为PDF格式。 软件特点 这款名为"五五Excel word批量转PDF"的工具体积小巧&#xff0c;不到2M大小&#xff0c;却能实现强大的批量转换功能&#xff0c…

面试150 基本计算器

思路 利用栈&#xff08;stack&#xff09;来保存进入括号前的计算状态&#xff08;包括当前计算结果和符号&#xff09;&#xff0c;以便在括号结束后正确恢复计算上下文。代码通过遍历字符串&#xff0c;识别数字、加号、减号和括号。遇到数字时构造完整数值&#xff1b;遇到…

源哈希(sh)解析

源哈希&#xff08;Source Hashing&#xff09;是一种负载均衡算法&#xff0c;它根据请求的源 IP 地址&#xff08;或其他标识符&#xff09;生成哈希值&#xff0c;然后根据这个哈希值将请求分配到特定的后端服务实例。这种方法常用于确保来自同一客户端的请求始终被路由到同…

axios的使用以及封装

前言&#xff1a; 在现代前端开发中&#xff0c;网络请求是不可避免的核心功能之一。无论是获取后端数据、提交表单信息&#xff0c;还是与第三方 API 交互&#xff0c;高效且可靠的 HTTP 请求库至关重要。axios 作为一款基于 Promise 的 HTTP 客户端&#xff0c;凭借其简洁的 …

github上部署自己的静态项目

前置知识1、要在github部署项目要提交打包后的静态文件(html,css&#xff0c;js)到仓库里2、我们看下github所提供给我们的部署方式有啥&#xff0c;如下所见&#xff1b;要么是/root文件夹&#xff08;就说仓库里全是打包后的产物&#xff1a;html,css&#xff0c;js要全部放到…

能源管理综合平台——分布式能源项目一站式监控

综合性的能源企业管理面临着项目多、分布散、信息孤岛等问题&#xff0c;分布式的多项目能源在线监控管理平台是一种集成了多个能源项目的数据采集、监控、分析和管理的系统。平台集成GIS能力&#xff0c;能够展示项目的整体分布态势&#xff0c;对不同地点、不同类型的能源项目…

修改阿里云vps为自定义用户登录

win系统上找到控制面板-->用户账户-->更改账户类型点击更改账户类型&#xff0c;此时我们看到vps的默认管理员账户Administrator。为了防止vps被别人使用默认账户Administrator攻击&#xff0c;我们添加一个用户账户&#xff0c;点击添加用户账户。 用户名建议奇葩点&…

Linux: perf: debug问题一例,cpu使用率上升大约2%;多线程如何细化cpu及perf数据分析

文章目录 前提面临的问题内核级别函数的差别继续debug总结根据pid前提 一个进程安置在一个CPU上,新功能上线之后,固定量的业务打起来,占用的CPU是42%。之前没有新功能的情况下,CPU占用是40%。差了大约2%。而且这个进程里的线程数非常多,有50多个线程。从差距看变化不大,…

计算阶梯电费

实现一个 Python 程序&#xff0c;根据使用的电量&#xff08;从控制台中让用户输入&#xff09;计算需要交的电费&#xff0c;电量分为两个阶梯&#xff0c;小于 200 度和大于 200 度&#xff0c;如果电量小于等于 200 度&#xff0c;电价就是 0.5 元/度&#xff0c;如果电量大…

替代MT6701,3D 霍尔磁性角度传感器芯片

KTH5502 是一款基于垂直霍尔技术的高精度绝对角度传感器芯片&#xff0c;支持全角度&#xff08;0–360&#xff09;测量。 芯片内部集成 X、Y 轴的垂直霍尔元件和 Z 轴的水平霍尔元件&#xff0c;能够同时感知磁场在 X、Y、Z 三个 方向的变化。得益于垂直霍尔技术优异的正交匹…

华为 Mate 80 影像配置揭秘:硬软双升

7 月 7 日&#xff0c;知名数码博主爆料了华为 Mate 80 系列的影像配置&#xff0c;引发广泛关注。从曝光信息来看&#xff0c;Mate 80 系列在影像方面延续华为的技术探索&#xff0c;通过硬件升级与算法优化&#xff0c;力图为用户带来更出色的拍摄体验。​ 爆料显示&#xff…

Android studio升级AGP需要注意哪些

deep seek回答&#xff1a; 深度思考完成 用时26秒 嗯&#xff0c;用户询问的是在Android Studio中升级AGP需要注意哪些事项。这个问题很实用&#xff0c;因为AGP升级确实是个容易踩坑的过程。让我看看搜索结果里有什么关键信息。 首先注意到搜索结果里有几个高可信度的资料专…

MapReduce数据处理过程2万字保姆级教程

目录 1. MapReduce 的核心思想:分而治之的艺术 2. Hadoop MapReduce 的架构:从宏观到微观 3. WordCount 实例:从代码到执行的完整旅程 4. 源码剖析:Job.submit 的魔法 5. Map 任务的执行:从分片到键值对 6. Shuffle 阶段:MapReduce 的幕后英雄 7. Reduce 任务的执行…

Rust单例模式:OnceLock的使用指南

想象一下你在构建一个需要全局数据库连接的Rust应用。传统语言里&#xff0c;单例模式常常伴随着锁的沉重和初始化竞态的焦虑。但在Rust的世界里&#xff0c;OnceLock就像个轻巧的守门人&#xff0c;只允许一次安全的通行。 简洁的OnceLock实现 看看这段代码如何优雅地解决单…

JavaScript性能优化实战:表格控件高效开发指南

引言 在现代Web应用开发中&#xff0c;电子表格功能已成为数据分析、报表展示等场景的核心需求。SpreadJS作为一款高性能的纯前端电子表格控件&#xff0c;能够完美兼容Excel文件格式&#xff0c;支持百万级数据量和复杂公式计算。然而随着数据规模的增长和业务逻辑的复杂化&a…

RWA(现实世界资产)代币化系统构建指南:合规、跨境与机构级解决方案

——金融科技公司机构市场拓展战略报告前言&#xff1a;RWA代币化的机构化浪潮与市场机遇 截至2025年6月&#xff0c;全球RWA&#xff08;Real World Assets&#xff09;链上规模突破240亿美元&#xff0c;3年增长超380%&#xff0c;成为仅次于稳定币的增速第二赛道。贝莱德、摩…

QML Label组件

QML中的Label组件是构建用户界面时最常用的文本显示控件之一&#xff0c;它继承自Text元素但提供了更丰富的UI特性和主题集成支持。本文将全面介绍Label的核心功能、属性配置、使用技巧以及与Text组件的区别&#xff0c;帮助开发者高效构建美观的文本界面。 Label组件基础 La…

使用 GDB 调试 Redis 服务进程指南

1. 准备工作 安装 GDB 在大多数 Linux 发行版上&#xff0c;执行&#xff1a; sudo apt-get update sudo apt-get install gdb确保有足够磁盘空间 Core dump 文件可能较大&#xff0c;请提前检查磁盘剩余空间&#xff1a; df -h .可选&#xff1a;使用 tmux 或 screen 为避免 S…

深度学习-环境准备

安装python&#xff0c;miniconda(最后步骤关于python环境变量部分全部勾选)&#xff0c;pycharm 关于离线安装numpy和matplotlib&#xff08;我的环境连不上网&#xff09; 我们先去 PyPI The Python Package Index 下载离线包 在搜索框搜索你的包名称&#xff0c;这里是 m…

记录在Windows系统用Python 3.12环境实现Nuitka过程

内容只提供Windows 10 与 Windows 11 下&#xff0c;搭建 Python 3.12 环境&#xff0c;并使用 Nuitka 将脚本打包为可执行文件的详细流程。全文分为以下几部分&#xff1a; 准备工作与系统要求 安装 Python 3.12 配置环境变量与 pip 创建虚拟环境&#xff08;推荐&#xff…