
文章目录引言基本数据处理 Excel 表格的读取 数据处理绘制双重饼图 定义颜色内圈饼图颜色外圈饼图颜色⭕ 绘制外圈饼图设置外圈饼图颜色和边框⭕ 绘制内圈饼图添加标签 标签的位置✍️ 写标签 完整的循环过程不足引言这篇文章我们将使用 MATLAB 复现在 2024高教社杯全国大学生数学建模竞赛C题论文展示 中绘制的双重饼图。具体来说我们将一步一步讲解如何读取 2024 高教社杯全国大学生数学建模竞赛 C 题附件 1.xlsx并根据附件 1.xlsx 中提供的数据绘制如下的双重饼图绘制的思路如下创建两个不同大小的坐标轴分别绘制外圈和内圈饼图然后通过控制坐标轴的Position实现大小嵌套。基本数据处理 Excel 表格的读取MATLAB 中提供多种方法来读取 Excel 表格包括readtable、xlsread、xlsreadtable等。其中readtable是 MATLAB R2013b 引入的现代函数返回表格数据类型。Treadtable(filename,Sheet,sheet,Range,range)优势自动识别列名支持混合数据类型更好的数据组织支持更多选项支持中文列名所以这里我们选用readtable函数来读取 Excel 表格。因为在附件 1.xlsx 中的列名存在中文所以在读取附件 1.xlsx 时我们需要指定VariableNamingRule属性为preserve。datareadtable(附件1.xlsx,VariableNamingRule,preserve);% 读取Excel保留中文列名head(data(:,1:3),5)% 查看前3列前5行数据 运行结果head(data(:, 1:3), 5) 地块名称 地块类型 地块面积/亩 _______ _________ __________ {A1} {平旱地} 80 {A2} {平旱地} 55 {A3} {平旱地} 35 {A4} {平旱地} 72 {A5} {平旱地} 68⚠️ 注意在读取附件 1.xlsx 时一定要记得先把附件 1.xlsx 复制到 MATLAB 工作目录下。2024高教社杯全国大学生数学建模竞赛真题下载链接 数据处理我们需要计算每种类型地的总面积同时需要把相同类型的地块分组保存起来方便后续的绘制双重饼图。第一步确定要提取的地块类型和绘制顺序target_names{水浇地,山坡地,梯田,平旱地};% 要提取的地块类型也是绘制顺序第二步分组保存地块类型并计算总面积接下来我们使用strcmp函数来判断每个地块的类型然后根据类型将地块分组保存起来并使用sum函数来计算每个类型的总面积。inner_valszeros(1,length(target_names));% 预分配各类型总面积outer_vals_cellcell(length(target_names),1);% 预分配各类型面积数组fori1:length(target_names)idxstrcmp(data.(地块类型),target_names{i});% 匹配该类型的行outer_vals_cell{i}data.(地块面积/亩)(idx);% 保存该类型各块面积inner_vals(i)sum(outer_vals_cell{i});% 计算该类型总面积endouter_valsvertcat(outer_vals_cell{:});% 拼成外圈饼图的数据注意点预先分配内存预先分配内存避免在循环中动态分配内存提高效率。中文列名的访问因为列名是中文所以在使用strcmp函数时需要使用双引号来包裹列名data.(地块类型)不能直接使用点号来访问列名。垂直拼接使用vertcat函数将所有类型的面积数值数组垂直拼接起来方便后续的绘制。这样我们就得到了需要的数据可以开始绘制双重饼图了。绘制双重饼图 定义颜色对于内外圈饼图我们需要采用不同方式来定义颜色内圈饼图颜色对于内圈饼图我们直接使用矩阵定义不同的颜色来表示不同的地块类型。inner_colors[% 内圈颜色每种地块类型一种颜色RGB0.88,0.48,0.44;% 红色/粉色0.58,0.48,0.72;% 紫色0.20,0.65,0.38;% 绿色0.96,0.80,0.20;% 黄色];如果你有其他颜色方案也可以根据需要修改inner_colors矩阵。或者你想知道更多科研绘图的颜色方案可以参考 科研色彩探索平台,里面有非常多的顶级期刊的配色方案。外圈饼图颜色对于外圈饼图我们采用渐变色来定义渐变的公式如下Color blend_factor × base_col ( 1 − blend_factor ) × [ 1 , 1 , 1 ] \text{Color} \text{blend\_factor} \times \text{base\_col} (1 - \text{blend\_factor}) \times [1, 1, 1]Colorblend_factor×base_col(1−blend_factor)×[1,1,1]其中b a s e _ c o l base\_{col}base_col为对应的内圈饼图的颜色b l e n d _ f a c t o r blend\_{factor}blend_factor为渐变因子取值范围为0.9 0.90.9到0.4 0.40.4按相同类型地块的数量进行线性采样。最后我们将所有类型的渐变色垂直拼接起来方便后续的绘制。。outer_colorscell(1,length(inval));% 预分配存放各类型渐变色fori1:length(inval)n_sublength(outer_vals{i});% 该类型的地块数量base_colinner_colors(i,:);% 以对应内圈颜色为基色blend_factorlinspace(0.9,0.4,n_sub);% 渐变因子 0.9→0.4 线性采样outer_colors{i}blend_factor*base_col(1-blend_factor)*[111];% 向白色渐变endouter_colorsvertcat(outer_colors{:});% 拼接所有渐变色⭕ 绘制外圈饼图绘制前我们需要先创建一个figure对象设置其颜色为白色位置为[100, 100, 600, 600]并创建一个坐标轴ax_outer设置其位置为[0.1, 0.1, 0.8, 0.8]作为外圈饼图的坐标轴。并设置DataAspectRatio属性为[1 1 1]确保饼图的大小是等比例的。figure(Color,w,Position,[100,100,600,600]);% 新建白色窗口600×600ax_outeraxes(Position,[0.10.10.80.8],DataAspectRatio,[111]);% 外圈坐标轴占窗口80%Position属性详解Position, [100, 100, 600, 600]表示figure对象左边缘距离电脑屏幕最左侧的距离为 100 像素图形窗口的底边缘距离电脑屏幕最底部的距离为 100 像素宽度为 600高度为 600。Position, [0.1, 0.1, 0.8, 0.8]表示ax_outer坐标轴左边缘距离图形窗口左侧边缘的距离占整个窗口宽度的 10%坐标轴的底边缘距离图形窗口底部边缘的距离占整个窗口高度的 10%坐标轴本身的总宽度占整个窗口宽度的 80%总高度占整个窗口高度的 80%。然后我们在坐标轴ax_outer上使用pie函数来绘制外圈饼图。使用repmat来使每个饼图的标签为空字符串从而隐藏标签。pie(ax_outer,outer_vals,repmat({},size(outer_vals)))% 绘制外圈饼图并隐藏标签设置外圈饼图颜色和边框接下来我们来设置外圈饼图的颜色和边框。使用findobj函数来找到ax_outer坐标轴上的所有类型的patch对象即多边形对象在这里就是我们刚刚绘制的饼图然后使用set函数来设置颜色和边框颜色。FaceColor设置多边形的填充颜色。EdgeColor设置多边形的边框颜色。LineWidth设置多边形的边框宽度。h_outerfindobj(ax_outer,Type,patch);% 获取饼图的多边形对象set(h_outer,{FaceColor},num2cell(flipud(outer_colors),2));% 设置填充颜色patch顺序倒序需翻转set(h_outer,EdgeColor,white,LineWidth,0.6);% 设置白色边框⚠️ 注意MATLAB 的pie函数返回的图形对象patch顺序是倒序的第一个对象对应最后一个数据因此设置颜色时必须用flipud()翻转颜色矩阵否则颜色分组会错位。我们使用num2cell()将颜色矩阵转换为元胞数组把颜色矩阵按行维度 2进行切分把每一行即每一个 RGB 颜色单独打包进一个 cell 单元里最终变成一个由多个 RGB 数组组成的元胞数组。我们可以先查看目前的效果如下所示接下来我们要在另一个坐标轴ax_inner上绘制内圈饼图直接覆盖外圈饼图形成我们想要的双重饼图的效果。⭕ 绘制内圈饼图我们定义内圈饼图的坐标轴ax_inner通过inner_size定义内圈饼图的大小并根据大小设置偏移量offset来确保内圈饼图的中心与外圈饼图的中心重合。计算的示意图如下inner_size0.56;% 内圈相对大小调小可让外圈更宽offset(1-inner_size)/2;% 偏移量使内外圈中心重合ax_inneraxes(Position,[offset offset inner_size inner_size],DataAspectRatio,[111]);% 内圈坐标轴现在我们在坐标轴ax_inner上使用pie函数来绘制内圈饼图。使用repmat来使每个饼图的标签为空字符串从而隐藏标签。并设置颜色为定义的内圈饼图的颜色边框颜色为白色边框宽度为 1.2。hpie(ax_inner,inner_vals,repmat({},size(inner_vals)));% 绘制内圈饼图保存句柄便于加标签h_innerfindobj(ax_inner,Type,patch);% 获取内圈多边形对象set(h_inner,{FaceColor},num2cell(flipud(inner_colors),2));% 设置颜色倒序需翻转set(h_inner,EdgeColor,white,LineWidth,1.2);% 设置白色边框pie的函数句柄h的奇数项是多边形对象偶数项是标签对象。我们这里将内圈饼图的函数句柄赋值保存下来方便添加标签。现在我们来查看一下效果添加标签现在离我们想要的效果已经很近了我们只需要添加标签即可。但是 MATLAB 中的pie的标签的位置是自动的我们需要手动调整标签的位置才能使标签位于扇形内。下面我们对于每一个扇形进行处理。 标签的位置我们先使用pie的函数句柄h获取第i个扇形的多边形对象的 x 坐标和 y 坐标。patch_objh(2*i-1);% 第i个扇形对象h的奇数项是多边形xpatch_obj.XData;% 扇形顶点x坐标ypatch_obj.YData;% 扇形顶点y坐标我们可以画出这个多边形来查看效果plot(x,y,r--o)结果如下可以看见MATLAB 就是通过绘制这些散点围成的多边形来实现绘制扇形的。接下来我们计算扇形的中心位置。首先我们计算扇形的中心角度计算方法如下我们先去掉 x 坐标和 y 坐标都为 0 的点因为这些点在圆心我们不需要计算。我们计算剩余点的平均点的角度即扇形的中心角度。代码如下mask~(x0y0);% 剔除圆心(0,0)处的点x_arcx(mask);% 弧线上各点x坐标y_arcy(mask);% 弧线上各点y坐标mid_angleatan2(mean(y_arc),mean(x_arc));% 弧线平均角度≈扇形中心角接下来因为pie在坐标系中默认半径为 1所以我们令标签所在的位置的半径为r_internal 0.55来保证标签位于扇形内。最后使用极坐标来计算标签的位置。r_internal0.55;% 标签半径保证位于扇形内text_xr_internal*cos(mid_angle);% 极坐标转直角坐标text_yr_internal*sin(mid_angle);✍️ 写标签标签属性位于pie函数句柄h的偶数项所以我们需要使用h(2*i)来获取标签对象。使用sprintf来格式化标签字符串包括标签的名称和百分比对于sprintf的用法可以查看文章 MATLAB 基本语法指南。最后使用set设置标签属性String标签字符串Position标签的位置HorizontalAlignment标签的水平对齐方式VerticalAlignment标签的垂直对齐方式Color标签的颜色FontSize标签的字体大小text_objh(2*i);% 第i个标签对象h的偶数项是标签str_labelsprintf(%s\n%.1f%%,target_names{i},...inner_vals(i)/total_val*100);% 标签内容类型名面积占比set(text_obj,...String,str_label,...Position,[text_x,text_y,0],...% 将标签放到扇形中心HorizontalAlignment,center,...VerticalAlignment,middle,...Color,black,...FontSize,8); 完整的循环过程r_internal0.55;% 标签半径扇形内total_valsum(inner_vals);% 总面积用于计算百分比fori1:length(inner_vals)patch_objh(2*i-1);% 第i个扇形对象xpatch_obj.XData;% 顶点x坐标ypatch_obj.YData;% 顶点y坐标mask~(x0y0);% 剔除圆心点x_arcx(mask);y_arcy(mask);mid_angleatan2(mean(y_arc),mean(x_arc));% 扇形中心角text_xr_internal*cos(mid_angle);% 标签位置极坐标转直角坐标text_yr_internal*sin(mid_angle);text_objh(2*i);% 第i个标签对象str_labelsprintf(%s\n%.1f%%,target_names{i},...inner_vals(i)/total_val*100);% 名称面积占比set(text_obj,...% 设置标签样式与位置String,str_label,...Position,[text_x,text_y,0],...HorizontalAlignment,center,...VerticalAlignment,middle,...Color,black,...FontSize,8);end现在我们来看看效果这样就已经实现了我们想要的效果。不足2024高教社杯全国大学生数学建模竞赛C题论文展示中的双重饼图我们在外围饼图颜色的变化没有完美复现出来。现在我还无法知道如何使用 MATLAB 实现完全一样的外围颜色效果。如果有深入研究的大佬欢迎指导。