ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Matplotlib绘图核心:Figure与Axes对象关系及subplot/subplots用法详解

Matplotlib绘图核心:Figure与Axes对象关系及subplot/subplots用法详解 1. 从一张白纸到一幅画布理解Matplotlib的绘图逻辑如果你刚开始用Python的Matplotlib库画图大概率会一头雾水。为什么一会儿是plt.plot()一会儿又是ax.plot()figure和axes到底是什么关系更别提subplot()和subplots()这两个名字相似但用法迥异的函数了。很多教程只告诉你“这么写就能出图”但没讲清楚背后的“世界观”。今天我就从一个画图老手的角度帮你彻底理清Matplotlib的绘图对象体系让你从“照着抄代码”进化到“心中有蓝图下笔如有神”。简单来说你可以把整个绘图过程想象成在现实中创作一幅画。figure图形就是你要作画的那张物理画布。而axes坐标轴则是画布上你规划出来的一个个独立的绘图区域每个区域都有自己的坐标系可以独立绘制折线图、柱状图等。plt.subplot()和plt.subplots()则是两种不同的“规划画布区域”的方法。理解清楚这几个核心对象你就能灵活驾驭任何复杂的多子图布局而不是被网上零碎的代码片段牵着鼻子走。2. 核心对象拆解Figure与Axes的父子关系要玩转Matplotlib必须先吃透figure和axes这两个对象。它们的关系是层次分明的。2.1 Figure承载一切的顶级容器figure对象你可以把它理解为一个画框或者画布。它是最顶层的容器一切图形元素都存在于某个figure之中。一个figure可以包含一个或多个axes子图以及一些画布级别的元素比如Figure的标题suptitle、画布尺寸figsize单位是英寸、背景色等。当你执行plt.figure()时就相当于申请了一块新的画布。它的一个重要特性是独占性。在非交互模式下比如在脚本中plt.plot()这类函数默认会在“当前活动的”figure和axes上作图。如果你不显式创建或指定Matplotlib会隐式地为你创建一个。但显式创建并持有figure对象的引用是写出清晰、可控代码的第一步。import matplotlib.pyplot as plt # 显式创建一个画布并拿到它的引用 fig plt.figure(figsize(10, 6)) # 画布宽10英寸高6英寸 # 此时fig是一张空白的画布上面还没有任何绘图区域。2.2 Axes真正的绘图战场axes对象才是我们平时绘制折线、散点、柱状图的地方。它不是一个简单的“坐标轴”axis而是一个包含坐标轴、刻度、标签、图例以及数据绘图区域在内的完整子系统。每个axes对象都隶属于一个figure对象。一个figure中可以包含多个axes它们以网格状排列就是我们常说的“子图”。每个axes都是完全独立的你可以在第一个子图里画正弦波在第二个子图里画散点图它们互不干扰。关键理解我们99%的绘图操作plot,scatter,bar,set_xlabel,set_title等都是作用在axes对象上的而不是figure对象。plt.plot()这种写法是Matplotlib提供的一种快捷方式它会自动找到“当前活动的”axes进行绘图。但在复杂的多子图场景中直接操作axes对象是更佳实践。# 在刚才创建的画布fig上添加一个子图axes ax fig.add_subplot(1, 1, 1) # 添加一个1行1列的第1个子图 # 现在我们在ax这个绘图区域上操作 ax.plot([1, 2, 3], [1, 4, 9]) ax.set_title(My First Axes) ax.set_xlabel(X) ax.set_ylabel(Y) plt.show()一个生动的比喻figure就像是一个桌面而axes就是桌面上摆放的一个个独立的绘图板。你可以在每个绘图板上画不同的内容。plt.subplot()和plt.subplots()就是两种在桌面上排列这些绘图板的方法。3. 两种布局策略subplot()的“指哪打哪”与subplots()的“整体规划”这是最容易混淆的一对函数。它们的目标都是创建子图axes但哲学和用法截然不同。3.1 plt.subplot()过程式、按需创建plt.subplot()函数是过程式的。它像是一个指挥棒告诉Matplotlib“接下来我要在画布的第N个位置上画画了”。它直接修改“当前活动的”figure和axes。它的经典语法是plt.subplot(nrows, ncols, index)。nrows, ncols将画布分割成几行几列。index子图的位置从左上角开始编号从左到右、从上到下递增。import matplotlib.pyplot as plt import numpy as np # 创建一个画布如果不存在plt会隐式创建 plt.figure(figsize(12, 4)) # 切换到2行2列的第1个位置左上角 plt.subplot(2, 2, 1) plt.plot(np.random.rand(10)) plt.title(Subplot (2,2,1)) # 切换到2行2列的第4个位置右下角 plt.subplot(2, 2, 4) plt.scatter(np.random.rand(10), np.random.rand(10)) plt.title(Subplot (2,2,4)) # 注意我们没有创建位置2和3的子图它们会是空白 plt.suptitle(Using plt.subplot() - Procedural Style) plt.tight_layout() plt.show()plt.subplot()的特点与坑点隐式创建如果当前没有figure它会自动创建一个。这有时会导致意想不到的新窗口弹出。状态切换调用它后“当前axes”就切换到了你指定的位置。之后所有的plt.xxx()命令如plt.title()都作用于这个新axes。容易覆盖如果你在同一个位置多次调用plt.subplot()后面的绘图会覆盖前面的。这既是特性也是坑需要小心。返回单一对象它返回的是单个axes对象对于指定位置。我的经验plt.subplot()在快速交互式探索如Jupyter Notebook中很方便但在编写正式脚本或函数时容易因为“状态”问题导致代码耦合度高不易维护。比如你在函数里调用了plt.subplot()可能会意外改变函数外部的绘图状态。3.2 plt.subplots()面向对象、一次性创建plt.subplots()函数是面向对象和声明式的。它的思路是“我先规划好整个画布的布局把所有的绘图板axes都准备好然后再分别往每个板子上画画”。这是目前更受推崇的实践方式。它的基本语法是fig, axes plt.subplots(nrows, ncols, ...)。它一次性创建了一个figure对象和一个包含所有axes对象的数组。返回的axes可能是一个单一对象当nrowsncols1时也可能是一个NumPy数组当有多行或多列时。import matplotlib.pyplot as plt import numpy as np # 一次性创建1个画布2行2列共4个子图 fig, axes plt.subplots(nrows2, ncols2, figsize(12, 8)) # 此时axes是一个2x2的数组可以通过索引访问每个子图 print(type(axes), axes.shape) # class numpy.ndarray (2, 2) # 现在我可以清晰地、独立地对每个axes进行操作 axes[0, 0].plot(np.arange(10), np.arange(10) ** 2) axes[0, 0].set_title(Axes [0,0]: Quadratic) axes[0, 1].bar([A, B, C], [3, 7, 2]) axes[0, 1].set_title(Axes [0,1]: Bar Chart) axes[1, 0].scatter(np.random.randn(100), np.random.randn(100), alpha0.5) axes[1, 0].set_title(Axes [1,0]: Scatter) axes[1, 0].set_xlabel(X label) axes[1, 1].hist(np.random.randn(1000), bins30, edgecolorblack) axes[1, 1].set_title(Axes [1,1]: Histogram) # 为整个画布设置一个总标题 fig.suptitle(Using plt.subplots() - Object-Oriented Style, fontsize16) # 自动调整子图参数使子图适合图形区域 fig.tight_layout() plt.show()plt.subplots()的优势代码清晰figure和所有axes的引用一目了然没有隐式的状态切换。易于批量操作axes是数组你可以用循环来批量设置属性或绘制类似图形。布局灵活通过plt.subplots()的参数可以轻松设置figsize、sharex/sharey共享坐标轴、constrained_layout更智能的布局等。避免覆盖每个axes对象独立操作互不干扰。一个关键技巧处理单子图情况。当nrows1, ncols1时axes不是一个数组而是一个单一的AxesSubplot对象。为了代码的一致性我习惯用ax np.array(axes).flatten()[0]或者直接通过索引axes如果它被保证是数组来访问但更简单的做法是直接使用返回的axes变量。# 创建单个子图 fig, ax plt.subplots() # 注意这里返回的ax是单个对象不是数组 ax.plot([1, 2, 3], [1, 2, 3]) ax.set_title(Single Axes)4. 深入subplots()高级参数与网格布局实战plt.subplots()的强大之处在于其丰富的参数可以应对各种复杂的排版需求。我们来深入几个最实用的。4.1 共享坐标轴让多图对比更专业在绘制具有相同X轴或Y轴的数据序列时共享坐标轴可以节省空间让对比更直观。通过sharex和sharey参数实现。fig, axes plt.subplots(nrows3, ncols1, figsize(8, 10), sharexTrue) # sharexTrue: 所有子图共享X轴。只有最底下的子图会显示X轴刻度标签。 time np.linspace(0, 10, 100) for i, ax in enumerate(axes): frequency i 1 ax.plot(time, np.sin(2 * np.pi * frequency * time)) ax.set_ylabel(fFreq{frequency} Hz) ax.grid(True, linestyle--, alpha0.6) # 只在最后一个子图设置X轴标签 if i len(axes) - 1: ax.set_xlabel(Time (s)) axes[0].set_title(Sine Waves with Shared X-axis) fig.tight_layout() plt.show()踩坑提醒设置了sharexTrue后如果你用ax.set_xlabel()为每个子图设置标签只有最后一个子图的标签会生效。通常的做法是只为最后一个子图设置X轴标签或者使用fig.supxlabel()为整个图形设置一个底部的公共标签。sharey同理。4.2 非均匀网格布局使用GridSpecplt.subplots()创建的是均匀网格。但有时我们需要一个大图搭配几个小图这就需要GridSpec。GridSpec是比subplot更底层的网格规划工具。import matplotlib.gridspec as gridspec fig plt.figure(figsize(12, 8)) # 定义一个3行3列的网格 gs gridspec.GridSpec(3, 3, figurefig) # 让第一个子图占据第一行所有列 ax_main fig.add_subplot(gs[0, :]) ax_main.plot(np.random.randn(100).cumsum(), linewidth2) ax_main.set_title(Main Trend (Row 0, All Columns)) # 在第二行第一列创建子图 ax_hist_x fig.add_subplot(gs[1, 0]) ax_hist_x.hist(np.random.randn(1000), bins30, orientationvertical) ax_hist_x.set_title(Distribution X) # 在第二行第二、三列创建子图跨两列 ax_scatter fig.add_subplot(gs[1, 1:]) ax_scatter.scatter(np.random.randn(200), np.random.randn(200), alpha0.6) ax_scatter.set_title(Scatter Plot (Row 1, Col 1-2)) # 第三行第一、二列创建子图 ax_detail fig.add_subplot(gs[2, :2]) x np.linspace(0, 4*np.pi, 200) ax_detail.plot(x, np.sin(x), labelsin) ax_detail.plot(x, np.cos(x), labelcos) ax_detail.legend() ax_detail.set_title(Detail View (Row 2, Col 0-1)) # 第三行第三列创建一个小图 ax_pie fig.add_subplot(gs[2, 2]) ax_pie.pie([30, 25, 20, 25], labels[A, B, C, D], autopct%1.1f%%) ax_pie.set_title(Pie Chart) fig.suptitle(Complex Layout with GridSpec) fig.tight_layout() plt.show()我的心得对于简单的均匀布局plt.subplots()是首选代码简洁。一旦布局变得复杂如跨行、跨列、大小不一GridSpec提供了像素级的控制能力。虽然学习曲线稍陡但掌握后能实现任何你能想到的排版。4.3 布局引擎tight_layout与constrained_layout子图多了标题、标签、刻度很容易重叠。Matplotlib提供了两个自动调整布局的工具。fig.tight_layout()这是一个“后处理”函数。在你绘制完所有内容后调用它它会自动调整子图之间的间距和子图与画布边缘的间距以避免重叠。它通过迭代尝试来找到一个合适的布局。参数如pad,w_pad,h_pad可以微调间距。fig, axes plt.subplots(2, 2) # ... 绘制各种图 fig.tight_layout(pad2.0) # 增加整体边距 plt.show()constrained_layoutTrue这是plt.subplots()或plt.figure()的一个参数。它是一种更现代、更强大的布局引擎在绘图过程中就持续计算布局通常比tight_layout效果更好、更稳定尤其对于包含颜色条(colorbar)、图例(legend)的复杂图形。# 推荐在创建图形时直接启用 fig, axes plt.subplots(2, 2, figsize(10, 8), constrained_layoutTrue) # 后续绘图无需再调用 tight_layout()选择建议对于新代码我强烈建议在创建图形时直接使用constrained_layoutTrue。如果因为某些原因比如兼容旧代码不能用再使用fig.tight_layout()作为备选。5. 面向对象编程实践为什么推荐Axes对象方法你可能见过两种绘图风格MATLAB风格/状态机风格plt.plot(x, y),plt.xlabel(X),plt.title(Title)面向对象风格ax.plot(x, y),ax.set_xlabel(X),ax.set_title(Title)在简单的脚本或交互式环境中第一种很便捷。但在任何严肃的项目、函数或模块中我100%推荐使用第二种面向对象风格。原因如下无状态更安全面向对象风格不依赖于“当前活动”的图形或坐标轴。你明确知道你在操作哪个对象避免了函数间意外的副作用。更清晰易维护代码逻辑一目了然。ax1做什么ax2做什么清清楚楚。功能更完整有些高级功能只在axes对象的方法中提供。例如ax.axvline()画垂直线比plt.axvline()更可预测位置。便于封装你可以轻松地将一个axes对象传递给函数让函数在指定的区域绘图实现代码复用。# 一个良好的实践示例将绘图逻辑封装成函数 def plot_sine_wave(ax, frequency, colorblue): 在给定的axes上绘制一个正弦波。 参数: ax: matplotlib.axes.Axes对象 frequency: 正弦波的频率 color: 线条颜色 x np.linspace(0, 2*np.pi, 200) y np.sin(frequency * x) line, ax.plot(x, y, colorcolor, linewidth2, labelff{frequency}) ax.set_xlim(0, 2*np.pi) ax.grid(True, alpha0.3) ax.legend() return line # 主程序 fig, (ax1, ax2) plt.subplots(1, 2, figsize(12, 4), constrained_layoutTrue) # 将ax1和ax2传递给函数完全控制绘图位置 plot_sine_wave(ax1, frequency1, colorroyalblue) ax1.set_title(Low Frequency) plot_sine_wave(ax2, frequency5, colorcrimson) ax2.set_title(High Frequency) fig.suptitle(Encapsulated Plotting with Axes Objects) plt.show()这种模式使得代码像搭积木一样每个函数负责一个具体的绘图任务主程序负责布局和组装极大地提升了代码的可读性和可维护性。6. 常见陷阱与性能优化即使理解了概念在实际使用中还是会遇到一些坑。这里分享几个我踩过的雷。6.1 内存泄漏与图形关闭在循环中创建大量图形而不关闭会导致内存占用持续增长。# 错误示范在循环中不断创建新图形而不关闭 for i in range(100): fig plt.figure() # 每次循环都创建新的figure对象 plt.plot([i, i1]) # 如果没有保存或显示这个figure对象会一直留在内存中 # plt.close(fig) # 必须关闭 # 正确做法1使用plt.close()显式关闭 for i in range(100): fig plt.figure() plt.plot([i, i1]) plt.savefig(fplot_{i}.png) # 如果需要保存 plt.close(fig) # 关闭当前图形释放内存 # 正确做法2更优复用同一个figure和axes fig, ax plt.subplots() for i in range(100): ax.clear() # 清除axes上的内容而不是创建新figure ax.plot([i, i1]) # 如果需要保存每一帧 fig.savefig(fframe_{i:03d}.png) # 最后再关闭 plt.close(fig)在Jupyter Notebook中每个cell的输出都会保留图形的引用。可以使用%matplotlib inline配合plt.close()来管理或者使用%matplotlib widget等交互式后端。6.2 坐标轴标签与标题重叠当子图很多或标题很长时很容易发生重叠。除了前面提到的tight_layout和constrained_layout还可以手动调整。fig, axes plt.subplots(2, 3, figsize(12, 8)) # 如果自动调整后仍有重叠可以手动设置子图间距 plt.subplots_adjust(left0.1, # 左边距 bottom0.1, # 底边距 right0.95, # 右边距 top0.9, # 顶边距 wspace0.3, # 子图间水平间距 hspace0.4) # 子图间垂直间距 # 然后绘制图形...plt.subplots_adjust()的参数是相对于图形宽高的比例0到1之间。通过微调这些参数可以解决大部分重叠问题。但constrained_layout通常能做得更好。6.3 大量数据绘图的性能当需要绘制数十万甚至上百万个数据点时直接使用plot可能会非常慢。降采样对于可视化趋势的折线图不需要每一个点都渲染。使用更高效的方法ax.plot(x, y, -, linewidth0.5)绘制细线。对于散点图使用ax.scatter时要小心每个点都是一个独立的Path对象。如果点非常多10k考虑使用ax.plot(x, y, o, markersize1, alpha0.5)它比scatter快。使用ax.hexbin六边形分箱图或ax.hist2d二维直方图来展示高密度散点的分布。开启快速样式对于交互式探索可以使用plt.style.use(fast)它会禁用一些美化效果以提升速度。# 绘制大量散点图的优化对比 import time np.random.seed(42) N 50000 x np.random.randn(N) y np.random.randn(N) fig, (ax1, ax2) plt.subplots(1, 2, figsize(12, 5)) # 方法1传统scatter (慢) start time.time() ax1.scatter(x, y, alpha0.1, s1) ax1.set_title(fax.scatter (N{N})\nTime: {time.time()-start:.2f}s) # 方法2使用plot模拟散点 (快) start time.time() ax2.plot(x, y, o, markersize1, alpha0.1, markeredgewidth0) ax2.set_title(fax.plot with markers (N{N})\nTime: {time.time()-start:.2f}s) plt.tight_layout() plt.show()在我的测试中当N50000时plot方法通常比scatter快一个数量级以上。对于纯粹展示分布hexbin是更好的选择。7. 从理解到精通构建自定义复合图表掌握了基础我们就可以玩些花样了。比如在一个axes里混合多种绘图类型或者创建更复杂的仪表板式图表。7.1 双Y轴与多Y轴有时需要在一个子图里展示两个量纲不同的序列。这时可以创建双Y轴。fig, ax1 plt.subplots(figsize(10, 6)) x np.arange(1, 11) # 第一个Y轴左侧 color1 tab:blue ax1.set_xlabel(Month) ax1.set_ylabel(Revenue (Millions $), colorcolor1) line1 ax1.plot(x, np.random.rand(10)*10 5, colorcolor1, markero, linewidth2, labelRevenue) ax1.tick_params(axisy, labelcolorcolor1) ax1.set_ylim(0, 15) # 创建共享X轴但独立的第二个Y轴右侧 ax2 ax1.twinx() color2 tab:red ax2.set_ylabel(Growth Rate (%), colorcolor2) line2 ax2.plot(x, np.random.rand(10)*30 10, colorcolor2, markers, linestyle--, linewidth2, labelGrowth Rate) ax2.tick_params(axisy, labelcolorcolor2) ax2.set_ylim(0, 40) # 合并图例需要一点技巧 lines line1 line2 labels [l.get_label() for l in lines] ax1.legend(lines, labels, locupper left) ax1.set_title(Company Performance: Revenue vs. Growth Rate) ax1.grid(True, alpha0.3) plt.show()注意twinx()创建了一个新的axes对象它与ax1共享X轴但拥有独立的Y轴。你可以分别对ax1和ax2调用plot、set_ylabel等。合并图例时需要手动处理。7.2 在图中插入子图Inset Axes有时我们需要在主图里放一个局部放大图这可以通过inset_axes实现。from mpl_toolkits.axes_grid1.inset_locator import inset_axes fig, ax plt.subplots(figsize(10, 6)) # 主图一个带有噪声的信号 x np.linspace(0, 10, 1000) signal np.sin(x) 0.1 * np.random.randn(len(x)) ax.plot(x, signal, linewidth1, alpha0.7, labelNoisy Signal) ax.set_xlabel(Time (s)) ax.set_ylabel(Amplitude) ax.set_title(Signal with Noise and Inset Zoom) ax.legend() ax.grid(True, alpha0.3) # 创建插入子图 (位置主图坐标系中的 [x0, y0, width, height]) # 这里选择放大x在[4, 6]区间 ax_inset inset_axes(ax, width40%, height30%, locupper right, bbox_to_anchor(0.1, 0.1, 0.8, 0.8), # 这个bbox是相对于父axes的 bbox_transformax.transAxes) zoom_mask (x 4) (x 6) ax_inset.plot(x[zoom_mask], signal[zoom_mask], colorcrimson, linewidth2) ax_inset.set_title(Zoom [4,6], fontsize10) ax_inset.grid(True, alpha0.5) # 在主图上用矩形标出放大区域 import matplotlib.patches as patches rect patches.Rectangle((4, min(signal[zoom_mask])), 2, max(signal[zoom_mask])-min(signal[zoom_mask]), linewidth1.5, edgecolorcrimson, facecolornone, linestyle--) ax.add_patch(rect) plt.show()inset_axes提供了非常灵活的方式来放置小图参数loc指定小图锚点在大图中的位置如upper rightwidth和height可以是像素值或字符串比例如40%。bbox_to_anchor和bbox_transform提供了更精细的控制。7.3 实战创建一个简单的仪表板最后我们把所有知识串起来创建一个包含多种图表类型的仪表板。# 生成模拟数据 np.random.seed(123) categories [Product A, Product B, Product C, Product D, Product E] sales np.random.randint(50, 200, sizelen(categories)) growth np.random.randn(len(categories)) * 0.1 0.05 # 增长率有正有负 time_series_x np.arange(12) # 12个月 time_series_y np.cumsum(np.random.randn(12)) 100 # 模拟随机游走 # 创建画布和复杂网格布局 fig plt.figure(figsize(16, 10), constrained_layoutTrue) # 使用GridSpec定义布局顶部一个宽图底部左右两个图 gs fig.add_gridspec(2, 2, height_ratios[1, 1.2], width_ratios[2, 1]) ax1 fig.add_subplot(gs[0, :]) # 顶部跨两列 ax2 fig.add_subplot(gs[1, 0]) # 左下 ax3 fig.add_subplot(gs[1, 1]) # 右下 # 1. 顶部时间序列折线图带填充 ax1.fill_between(time_series_x, time_series_y - 10, time_series_y 10, alpha0.2, colorskyblue, label±10 Range) ax1.plot(time_series_x, time_series_y, colordodgerblue, markero, linewidth2.5, labelMonthly Sales) ax1.set_title(Monthly Sales Trend with Confidence Band, fontsize14, fontweightbold) ax1.set_xlabel(Month) ax1.set_ylabel(Sales Volume) ax1.legend(locupper left) ax1.grid(True, linestyle--, alpha0.6) # 设置X轴刻度为月份 ax1.set_xticks(time_series_x) ax1.set_xticklabels([fMonth {i1} for i in time_series_x], rotation45) # 2. 左下柱状图销售额 折线图增长率双Y轴 color_bar steelblue color_line darkorange # 柱状图 bars ax2.bar(categories, sales, colorcolor_bar, alpha0.8, labelSales Volume) ax2.set_xlabel(Product) ax2.set_ylabel(Sales Volume, colorcolor_bar) ax2.tick_params(axisy, labelcolorcolor_bar) ax2.set_title(Sales Volume Growth Rate by Product, fontsize14, fontweightbold) # 为柱状图添加数值标签 for bar in bars: height bar.get_height() ax2.text(bar.get_x() bar.get_width()/2., height 3, f{int(height)}, hacenter, vabottom, fontsize9) # 创建第二个Y轴用于增长率折线 ax2_twin ax2.twinx() line ax2_twin.plot(categories, growth, colorcolor_line, markers, linewidth2.5, labelGrowth Rate) ax2_twin.set_ylabel(Growth Rate, colorcolor_line) ax2_twin.tick_params(axisy, labelcolorcolor_line) # 合并图例 lines_labels [ax2.get_legend_handles_labels(), ax2_twin.get_legend_handles_labels()] lines, labels [sum(lol, []) for lol in zip(*lines_labels)] ax2.legend(lines, labels, locupper right) # 3. 右下饼图销售额占比 # 为了突出显示让销售额最大的那块“爆炸”出来 explode [0.1 if s max(sales) else 0 for s in sales] wedges, texts, autotexts ax3.pie(sales, explodeexplode, labelscategories, autopct%1.1f%%, shadowTrue, startangle90, colorsplt.cm.Set3(np.arange(len(categories))/len(categories))) ax3.set_title(Sales Distribution by Product, fontsize14, fontweightbold) # 美化饼图的百分比文本 for autotext in autotexts: autotext.set_color(white) autotext.set_fontweight(bold) # 为整个仪表板设置一个总标题 fig.suptitle(Product Performance Dashboard, fontsize18, fontweightbold, y1.02) plt.show()这个例子综合运用了GridSpec、双Y轴、多种图表类型折线、填充、柱状、饼图、数据标签、图例合并等技巧。关键在于前期对布局的规划GridSpec和对每个axes对象的独立操作。当你能够这样自如地操控figure和axes时Matplotlib就不再是限制你表达的障碍而是帮助你实现任何可视化想法的强大工具。理解figure和axes的关系并在subplots()的面向对象范式下工作是写出高效、清晰、可维护的Matplotlib代码的基石。从“能用”到“精通”这一步跨越带来的不仅是代码质量的提升更是解决问题能力的飞跃。下次当你需要画图时不妨先花一分钟在纸上勾勒一下布局我需要几个子图它们之间有什么关系共享坐标轴吗想清楚这些代码自然就水到渠成了。
返回列表