首页 / 资讯中心 / 文章详情

MATLAB多图例同一行:Orientation与NumColumns使用指南

MATLAB多图例同一行:Orientation与NumColumns使用指南 ★ FEATURED ARTICLE
前阵子有朋友发来一张图图本身没什么毛病就是右上角那堆图例像记账一样从上往下列了七八行把实验曲线都挤到左边去了。这种问题几乎每个用MATLAB画图的人都碰到过——线画了一堆legend默认竖着排占地方不说打印或者贴到报告里还显得很业余。这篇文章就专门聊一件事MATLAB中怎样把多个图例放在同一行。这里“多个图例”实际涵盖两种常见理解一种是一个图例里的多个条目横着排成一条线另一种是多个子图或独立图例框在水平方向上对齐。两种方向我都会给出能直接复制的做法。无论你是刚入门的新手还是经常用MATLAB出图写报告的老用户下面这些方法都可以拿来即用。1. 先搞清楚你要的是“排成一排”还是“排成一行”1.1 一个图例里的多个条目横向排开如果你现在脑子里想的是这样的画面右上角本来是sin(x)、cos(x)、sin(2x)三个标签从上到下堆成一个竖条你希望它们变成从左到右的一个横条。那这个问题的本质是调整legend对象自身的排布方向不涉及任何额外的图形对象也是大多数人搜“多个图例放在同一行”时的真实需求。这种需求在数据可视化里非常典型。比如你同时画了实验组的真实曲线、对照组曲线、拟合曲线可能还加了一条参考线。四条线各有名字默认情况下MATLAB会把这些名字按照图例项的先后顺序从上到下罗列在同一个矩形框里。条目少时还好条目一旦超过五六个图例框会变得又高又窄直接在绘图区里“开出一列清单”把曲线的有效区域压得更小。1.2 多个图例框/子图图例水平对齐但还有一类问法也很常见一个figure里有几个subplot每个子图右下角都有自己的小图例你希望这些小图例都放在各自子图的顶部并且视觉上处在同一条水平线上或者你想在同一个绘图区域同时放两个图例块比如左半边放实验组说明右半边放对照组说明。这类需求就不是调整一个legend对象那么简单了它要处理的是“多个图例对象之间的对齐关系”。很多人一开始没分清这两种情况直接去搜“如何把图例横过来”然后发现代码套在自己场景里怎么都不对劲。原因就在这里你需要的不是让某个图例的条目排成一条横线而是让多个图例框在画布上排成一条横线。需求理解典型画面解决方向一个图例内的多个条目横排三条曲线的标签从左到右排成一排调整单个legend的Orientation或NumColumns多个图例框水平对齐多个子图的图例都摆在顶部同一水平线统一Location为northoutside或手动微调Position怎么快速判断你属于哪一种如果你只是想在同一个坐标轴里让标签紧凑一点不挡数据那是第一种如果你打开的是tiledlayout或subplot布局想整理的是多个框那是第二种如果你的子图里每个图例还想再省点竖向空间那就把两种方法结合着用。下面先从最简单的第一种说起。2. 最基础的实现把图例项方向改成horizontalMATLAB把图例当成一个独立的图形对象它有一个属性叫Orientation默认值是vertical也就是图例项自上而下排列。把Orientation改成horizontal图例项就会首尾相连排成一条横线。这是最老也最通用的基础方案哪怕你用的MATLAB版本比较旧这个属性也一直存在。2.1 最小可运行代码直接看一段能跑的代码x 0:0.01:2*pi; y1 sin(x); y2 cos(x); y3 sin(2*x pi/4); figure(Color, w); plot(x, y1, b-, LineWidth, 1.6); hold on; plot(x, y2, r--, LineWidth, 1.6); plot(x, y3, k-., LineWidth, 1.6); lgd legend({sin(x), cos(x), sin(2x\pi/4)}); lgd.Orientation horizontal;上面这段代码里关键就是最后一行lgd.Orientation horizontal。如果你想在创建图例时顺便指定方向也可以写成legend(..., Orientation, horizontal)效果完全一样。我习惯先拿到lgd句柄再改属性是因为后续还要继续调整位置、字号、内部线条宽度拿着句柄操作更统一。2.2 设置之后图例位置会变记得重新摆放设成horizontal之后你会发现图例框的位置经常和竖排时不在同一个地方。默认情况下图例通常挂在坐标轴右上角改成横向以后图例框会一下子变得又宽又扁如果正好横在曲线密集的区域就会把后半段数据盖住。所以设置方向之后的第一件事是重新摆放图例位置。常用的位置有这么几个north坐标轴内部上方居中适合多条曲线集中在图例下方的情况。northoutside坐标轴外部上方横排图例很推荐这个位置因为图例会跑到绘图区域外面完全不挡数据。south坐标轴内部下方居中底部有空地时可以用。我个人的偏好是尽量用northoutside。配合Orientationhorizontal图例就横在坐标轴上方曲线再怎么密都不会被图例盖住。设置方式很简单lgd.Orientation horizontal; lgd.Location northoutside;有一个小提示northoutside在比较老的版本里也支持不挑版本。它和westoutside、eastoutside这类“出口式”位置属于同一类MATLAB会自动调整坐标轴的范围腾出空间给图例展示而不是让图例硬叠在数据上面。2.3 基础方案的局限性这个方案看着简单但有一个明显短板它只管“方向”不管“怎么换行”。一旦条目超过五六个或者某个标签特别长横向图例就会一直往右延伸直到超出坐标轴边界被画布边缘直接切掉。换句话说它做不到“固定做成两列”或者“每行刚好五个条目”。另外Orientationhorizontal在条目很多时也不太好控制视觉效果。三条四条线横排几乎不会出问题但如果有个八个条目你希望成两行四列地排列纯靠Orientation就没法实现了。这时候就需要往下看NumColumns属性——当然如果只是三四条线横着摆Orientation已经完全够用不必为了追求新写法而额外折腾。3. 新版本更优雅的解法用NumColumns控制列数3.1 NumColumns的常规用法NumColumns是图例对象上专门用来控制“列数”的属性。我记忆里这是R2021a及之后的版本才正式支持的公共属性也就是说老版本工具包里没有它。如果你在点lgd.NumColumns时提示找不到属性别怀疑自己写错了先查一下版本然后退回第2章的Orientation方案。基本用法是这样x 0:0.01:2*pi; y1 sin(x); y2 cos(x); y3 sin(2*x pi/4); y4 cos(2*x); figure(Color, w); plot(x, y1, x, y2, x, y3, x, y4, LineWidth, 1.5); lgd legend(sin(x), cos(x), sin(2x), cos(2x)); lgd.NumColumns 4; lgd.Location north; lgd.Box off;设置NumColumns 4后四个图例条目会按照4列排开视觉上就是一行。如果你有八个条目设置成NumColumns 4得到的结果是4列2行的网格。这里有个特别容易翻车的细节MATLAB是按“列优先”填充的不是按行优先。也就是说第1列从上往下是第1条和第5条第2列是第2条和第6条以此类推。你在检查标签顺序时不要想当然地以为是第一行排前四个、第二行排后四个。3.2 “正好一行”的技巧列数等于条目数想让所有条目都在同一行最直接的办法就是把NumColumns设成条目总数。比如有五条曲线就写lgd.NumColumns 5;这样不管图例框最终多宽所有标签都被压到同一行里。不过这里有个坑如果条目一共五个你手滑设成6图例会保留一个空白列框体反而变得更宽、更空。所以设列数之前先数一数标签个数不是列数越多越好看。提示NumColumns设置的是“列数”不是“每列的条目数”。想一行排五个列数就取5想排两行列数就取“条目总数除以2”。有余数也没关系最后一行缺位是正常的稍微调整图例位置就能接受。3.3 和Orientation、Layout之间的关系很多第一次用的人都想把NumColumns和Orientation一起调结果发现要么报错要么设置完一个之后另一个自动变回默认值。原因是Orientation管的是每一列内部条目的排列方向NumColumns管的是整体排成几列这两个参数同时设置成非默认值在新旧版本里的行为都不太一致很容易互相覆盖。稳妥的做法是二选一想要所有条目首尾相连成一条横线就用Orientationhorizontal别再动NumColumns。想要一个规则的多行多列网格就用NumColumns n此时Orientation保持默认的vertical。如果你的版本支持Layout属性也可以试试lgd.Layout flow。这个布局模式会让图例条目根据可用空间自动换行省去手动数条目、算列数的麻烦。但就我个人使用体验来说flow在“精确控制每行列数”这件事上不如NumColumns直观所以我正式出图时还是手动指定列数的场景更多。3.4 让横向图例更精致的小参数想把图例排得干净除了列数还有几个常用参数可以调。第一个是ItemTokenSize它控制每条图例前面那段线条和标记区域的宽度。默认值比较大横排之后尤其显得笨重我一般会设小一点lgd.ItemTokenSize [16 16];第二个是Box默认图例有一个矩形边框。横排的时候如果条目多框线会把画面框得很挤直接lgd.Box off去掉边框会更清爽。第三个是FontSize图例字号建议和坐标轴刻度字号保持一致一般10到12就够了太大容易让标签换行太小打印出来看不清。另外我非常推荐在plot时给每条线设置DisplayName之后直接调用legend()让MATLAB自动读取标签。好处是图例标签始终跟着曲线走后期改线型、颜色、线宽都不容易出现标签和曲线错位的问题plot(x, y1, b-, LineWidth, 1.5, DisplayName, 信号A); hold on; plot(x, y2, r--, LineWidth, 1.5, DisplayName, 信号B); lgd legend(); lgd.NumColumns 2;在较新的版本里这段代码完全没问题。如果版本比较旧legend()不带参数不一定认DisplayName那就老老实实把标签列表写全。4. 进阶场景子图图例合并与水平对齐4.1 多个子图的曲线合并进同一个图例有时候你的曲线分布在不同的子图里但想把它们的图例合并成一块统一放在最上面的子图里。比如有两个子图第一行画y1第二行画y2最后在第一个子图里做一个包含两条线的横向图例。做法是先把每个子图的曲线句柄都收回来再一次性传给legendfigure(Color, w); t tiledlayout(2, 1); ax1 nexttile; p1 plot(x, y1, b-, LineWidth, 1.5); title(ax1, 第一行); ax2 nexttile; p2 plot(x, y2, r--, LineWidth, 1.5); title(ax2, 第二行); lgd legend(ax1, [p1, p2], {y1, y2}, NumColumns, 2);这里的关键点是把p1和p2组成一个句柄数组传给legend目标坐标轴则用ax1指定。这样两个子图各自不会出现重复图例整个figure看起来干净很多。如果你的MATLAB版本对“跨坐标轴传句柄”提示不支持临时替代办法是只在某个子图里额外画一条隐藏曲线或者把需要展示的曲线都画到同一个坐标轴上再做图例具体取决于你画图需求有多复杂。4.2 每个子图图例统一横排并锁在同一水平线另一个常见场景正好相反每个子图都要保留自己的图例只是希望这些图例在视觉上水平对齐并且都不要占太多竖向空间。做法是先给每个子图分别创建图例然后统一设置方向和位置for k 1:2 lgd(k) legend(ax(k)); lgd(k).Orientation horizontal; lgd(k).Location northoutside; end前提是每个子图里的曲线都设置了DisplayName或者你手动传入对应标签。设置了northoutside之后图例会跑到子图上方外侧MATLAB会自动压缩子图高度腾出位置所以两个子图的图例自然落在同一条水平线上。这个方案在并列对比多个数据组时特别实用每个子图上方一行对应的图例读者一眼就能看到彼此关系。4.3 用Position微调把图例挪到标题旁边如果你不想用northoutside或者希望图例和标题处在同一个视觉行那就得手动改Position。Position是一个四元素向量大致对应[x y width height]单位是归一化坐标范围0到1。手动设置前我建议先读一次当前值再在当前值基础上做小幅偏移比自己凭空填一个数值稳得多lgd.Units normalized; p lgd.Position; lgd.Position [p(1)0.02, p(2), p(3), p(4)]; % 向右挪一点想把图例和标题放在同一行一个比较可行的套路是标题靠左对齐图例手动挪到右上角附近。title(信号对比, HorizontalAlignment, left); lgd.Location north; lgd.Position [0.5, 0.88, 0.35, 0.04];上面的坐标是我在自己机器上试出来的大致值。不同布局里图片大小、字体、标签长度都不一样直接照抄不一定贴合但思路是通用的先设置Location到north或northoutside再用Position做增量微调效果不满意就继续改x和y直到图例和标题视觉上落在同一行。5. 实测踩过的坑和一个可以直接套用的模板5.1 四个最容易踩的坑现象常见原因处理办法图例空白或只显示部分条目legend创建之后继续用hold on添加新曲线或AutoUpdate被关闭给每条线设DisplayName最后统一legend()显式传入曲线句柄设置NumColumns没反应或直接报错版本太旧或与Orientation/Layout同时设置冲突版本不够退回Orientation方案只保留一种排布参数图例跑到画布外导出时被切掉横向图例太长画布宽度不够或northoutside超出figure边界拉宽figure窗口用exportgraphics导出避免print直接切边标签出现下划线下标或中文乱码TeX解释器把_、^当成格式标记系统缺中文字体设置Interpreternone指定FontName为中文字体第一个坑最隐蔽。很多人是先画曲线后来才想起加图例加完发现只有第一条线有名字后面hold on加的线全都不显示。原因多半是图例的AutoUpdate被关掉了或者版本不支持自动更新。解决办法是给每条线都设置DisplayName最后统一用legend()重新生成一次并确认AutoUpdate处于on状态。第二个坑碰到过的人更多。写完lgd.NumColumns 4结果图例没有任何变化甚至报错说找不到这个属性。不用纠结先查版本。老版本没有这个参数只能升级或者退回第2章的Orientation方案新版本里也尽量避免同时设置NumColumns和Layoutflow二者在很多版本中会互相覆盖。第三个坑主要在导出图片时爆发。窗口宽度不够的时候横向图例很容易超出右边界。先执行set(gcf, Position, [100 100 900 500]);把画布拉宽再用exportgraphics(gcf, output.png, Resolution, 300);导出。exportgraphics比老的print对布局更友好能减少很多图例被切边的问题。5.2 综合模板五条曲线一行图例把上面的经验汇总成一个可以直接套用的模板。这个模板适合“一次画出多条曲线并把所有图例条目压到同一行”的常见场景。% 模拟五条曲线 x 0:0.01:2*pi; y [sin(x); cos(x); sin(2*x); cos(2*x); sin(x)cos(x)]; figure(Color, w, Position, [100 100 1000 480]); plot(x, y, LineWidth, 1.5); grid on; xlabel(时间 / s); ylabel(幅值); title(五条曲线的图例一行排列, FontSize, 12); lgd legend(信号1, 信号2, 信号3, 信号4, 信号5); lgd.NumColumns 5; lgd.Location north; lgd.Box off; lgd.FontSize 11; lgd.ItemTokenSize [16 16]; lgd.Interpreter none; exportgraphics(gcf, legend_one_line.png, Resolution, 300);plot(x, y)一次画出五行数据图例顺序和y的行顺序一致所以手动写五个标签不会错位。NumColumns 5让五行条目正好排成一行。如果线条增加到七条八条我一般会把NumColumns改成条目总数的一半让图例变成两行布局比硬压成一行更协调。导出的PNG分辨率300dpi论文和报告基本够用。我自己现在画多曲线的图几乎固定用DisplayNamelegend()NumColumns这套组合。标签跟曲线绑定排版可控后期改动线型或颜色都不容易错位。最后分享一个小习惯图例放到同一行本质是为了节省空间、方便读者对照不是逼着把标签硬塞成一排。如果某个标签超过十个字我会先把它缩短比如“高温组-三次重复-第2批”改成“高温组第2批”。标签短了一行才放得下视觉上也干净得多。这种排版上的取舍MATLAB帮不了你但能让你的图一下子专业很多。
阅读完成 · 觉得有帮助?
咨询建站