Matplotlib多排多列子图布局全解析:从基础subplots到高级GridSpec
2026/8/26 4:42:15 网站建设 项目流程

1. 项目概述:从“一张图”到“一面墙”的思维跃迁

在数据分析和科学计算的世界里,我们常常需要将多个图表并排展示,以便进行直观的对比、关联分析或展示同一数据的不同维度。无论是比较不同算法在多个数据集上的表现,还是观察一个时间序列数据在不同指标上的变化,抑或是展示一张图片经过不同滤波器处理后的效果,并列图(或称多子图)都是不可或缺的利器。很多朋友初学matplotlib时,掌握了plt.plot()画单张图,但一到需要画“一面墙”的图表时就感到无从下手,代码变得冗长混乱,子图大小不一,对齐困难。这背后的核心需求,其实是如何高效、清晰、自动化地组织多个可视化单元,形成一个逻辑严谨、视觉美观的复合图表。

matplotlibsubplot系统正是为此而生。它允许我们在一个画布(Figure)上创建多个坐标轴(Axes)网格,每个坐标轴都是一个独立的绘图区域。本项目标题“并列图的画法,多排多列”直指这一核心功能的高级应用:超越简单的单行或单列排列,实现任意行、任意列的复杂网格布局,并在此过程中解决对齐、共享坐标轴、统一格式等实际问题。结合热词matplotlib colormapnumpy 计算matplotlib画的方块邻居元素之和,我们还可以延展出更丰富的场景,例如在多个子图中使用一致的色彩映射进行数据对比,或者将子图本身视为一个网格,用numpy计算其“邻居”关系以进行特殊布局或数据交互展示。

本文将从零开始,拆解matplotlib实现多排多列子图的四种核心方法:基础的plt.subplots、灵活的GridSpec、面向对象的add_subplot以及极简的plt.subplot。我会分享大量实际项目中积累的配置技巧、排版心法以及那些官方文档里不会写的“坑”与解决方案。无论你是需要制作论文插图、项目报告还是交互式仪表板的原型,掌握这套“组合拳”,都能让你从容应对。

2. 核心思路与方案选型:四种武器,各显神通

面对“多排多列”的需求,matplotlib提供了多种工具,每种都有其最佳适用场景。选择不当,要么代码繁琐,要么无法实现特定效果。理解它们的设计哲学和能力边界,是高效绘图的第一步。

2.1 全能起点:plt.subplots,一键创建网格

这是目前最推荐、也是最常用的方法。plt.subplots()函数一次性返回一个图窗(Figure)对象和一个包含所有坐标轴(Axes)对象的Numpy数组。它的思维模型是“先搭好舞台(网格),再请演员(数据)上台”。

import matplotlib.pyplot as plt import numpy as np # 创建一个2行3列的子图网格 fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(12, 8)) # axs 是一个 2x3 的数组,可以通过 axs[row, col] 索引访问 axs[0, 0].plot([1, 2, 3], [1, 4, 9]) axs[0, 0].set_title('第一个子图')

为什么首选它?其最大优势在于返回的Axes对象是规整的数组,便于我们通过循环进行批量操作。例如,为所有子图设置统一的y轴标签,只需一个循环:for ax in axs.flat: ax.set_ylabel('Value')。这对于格式化大量子图来说,能极大减少重复代码。

关键参数解析:

  • nrows, ncols: 定义网格的行数和列数。
  • figsize: 定义整个图窗的尺寸(宽,高,单位英寸)。这是最容易踩坑的地方之一。很多人只关注子图数量,忽略了总尺寸,导致每个子图过于拥挤。一个经验公式:假设每个子图希望有4x3英寸的绘图空间,那么对于2x3的网格,figsize至少应设为(3*4, 2*3) = (12, 6)。当然,还需要为标签、标题、刻度留出边距。
  • sharex, sharey: 设置为True'all'时,所有子图共享x轴或y轴,刻度标签会自动隐藏,避免重复,图表更整洁。这在对比具有相同量纲的数据时非常有用。
  • constrained_layout: 建议设置为True。这是一个自动调整子图间距、标签、标题位置的布局引擎,能解决大部分手动调整plt.tight_layout()都难以处理的复杂布局冲突,是“懒人”福音。

注意sharexsharey虽然方便,但当你需要为不同子图设置不同类型的坐标轴(如一个用线性坐标,一个用对数坐标)时,就不能使用。此时需要在创建后手动设置。

2.2 精细控制:GridSpec,布局艺术家

当你的需求超越了简单的等分网格,比如需要某个子图跨越多行多列(合并单元格),或者子图之间的宽度比、高度比需要特殊设置时,plt.subplots就力不从心了。这时,GridSpec闪亮登场。它的思维模型是“定义一块画布的网格分割规则,然后按需在指定区域放置坐标轴”。

GridSpec本身不直接创建坐标轴,它定义了一个网格的规格。你需要结合Figure.add_subplot()来在网格的特定位置创建坐标轴。

fig = plt.figure(figsize=(10, 6)) # 定义一个3行3列的网格,并指定宽度和高度的比例 gs = fig.add_gridspec(nrows=3, ncols=3, width_ratios=[2, 1, 1], height_ratios=[1, 2, 1]) # 创建一个占据第一行所有列的Axes ax_main = fig.add_subplot(gs[0, :]) ax_main.plot(np.random.randn(100).cumsum()) ax_main.set_title('主时间序列') # 创建一个占据第二、三行第一列的Axes (跨两行) ax_hist = fig.add_subplot(gs[1:, 0]) ax_hist.hist(np.random.randn(1000), bins=30, orientation='horizontal') ax_hist.set_title('分布直方图') # 在第二行第二、三列创建两个小图 ax_scatter1 = fig.add_subplot(gs[1, 1]) ax_scatter2 = fig.add_subplot(gs[1, 2]) # ... 绘制散点图 # 在第三行第二、三列创建两个小图 ax_box1 = fig.add_subplot(gs[2, 1]) ax_box2 = fig.add_subplot(gs[2, 2]) # ... 绘制箱线图

为什么需要GridSpec它提供了像素级(更准确地说是比例级)的布局控制。width_ratiosheight_ratios参数让你可以轻松实现“主图大、辅图小”的仪表板式布局,或者创建复杂的报告模板。这在制作需要发表的高质量图表时尤其重要。

2.3 面向对象:fig.add_subplot,手动构建

这是最基础、最面向对象的方法。直接创建一个Figure对象,然后手动调用add_subplot来添加每一个坐标轴。你需要指定子图在网格中的位置,参数是一个三位整数(如231)或三个独立参数(行,列,索引)。

fig = plt.figure(figsize=(8, 6)) ax1 = fig.add_subplot(2, 2, 1) # 2行2列,第1个位置 ax2 = fig.add_subplot(2, 2, 2) # 2行2列,第2个位置 ax3 = fig.add_subplot(2, 2, 3) # 2行2列,第3个位置 ax4 = fig.add_subplot(2, 2, 4) # 2行2列,第4个位置

它的用武之地在哪?这种方法在子图数量较少且布局规则时,代码直观。但在子图很多时,需要为每个子图写一行创建代码,非常繁琐。此外,它不方便实现跨行跨列的复杂布局。因此,在现代matplotlib编程中,它更多作为GridSpec的搭档出现(如上例),或者在一些遗留代码中看到。

2.4 传统速写:plt.subplot,单线脚本的遗产

这是MATLAB风格的过程式编程接口,会直接在当前图形(gcf)上操作。它返回一个坐标轴对象,并自动将当前绘图上下文切换到该坐标轴。

plt.figure(figsize=(8, 6)) plt.subplot(2, 2, 1) plt.plot([1,2,3], [1,2,3]) plt.title('Plot 1') plt.subplot(2, 2, 2) plt.scatter([1,2,3], [3,2,1]) plt.title('Plot 2') # ... 以此类推

为什么不推荐?它混合了全局状态(plt)和对象操作,在复杂的脚本或函数中容易导致意想不到的副作用,比如不小心在错误的子图上绘图。代码的可读性和可维护性较差。对于任何严肃的项目,都建议使用面向对象的API(前三种方法)。

方案选型总结:

  • 追求效率和整洁:无脑用plt.subplots,配合constrained_layout=True
  • 需要复杂的不规则布局:上GridSpec
  • 学习或维护旧代码:了解fig.add_subplotplt.subplot
  • 简单快速画几个图plt.subplots依然是首选,plt.subplot可作为备选但需谨慎。

3. 核心细节解析与高阶技巧

掌握了基本方法,我们来看看如何让这“一面墙”的图表不仅画出来,还要画得专业、美观。这里涉及到共享坐标轴、统一格式、处理刻度标签重叠等实际问题。

3.1 共享坐标轴的智慧与陷阱

共享坐标轴(sharex/sharey)能自动对齐多个子图的坐标轴范围,并隐藏重复的刻度标签,让图表看起来是一个整体。

fig, axs = plt.subplots(2, 3, figsize=(12, 8), sharex=True, sharey=True) # 现在所有子图都有相同的x轴和y轴范围 # 只有最下面一行子图显示x轴标签,只有最左边一列子图显示y轴标签

实操心得:共享轴在对比数据时非常有用,但要注意一个常见问题:当数据量级差异巨大时,共享轴会导致量级小的数据在图上变成一条平坦的直线。例如,一个子图数据范围是[0, 100],另一个是[0, 1],如果共享y轴,后者几乎看不见。此时有两种策略:

  1. 不共享轴,让每个子图自适应范围。但这样对比性减弱。
  2. 使用对数坐标set_yscale('log'))来压缩量级差异。这需要数据都为正数。
  3. 使用双y轴twinx()/twiny()),但这适用于单个子图内对比两个量纲不同的序列,对于多个子图场景不直接适用。

更通用的做法是,在需要精细化控制时,可以创建时不共享,然后手动设置某些子图的坐标轴不可见,再使用axs[0,0].get_shared_x_axes().join(axs[0,0], axs[0,1])这样的方法进行选择性共享。

3.2 全局样式与批量操作:效率倍增器

当你有几十个子图需要设置相同的标签字体、刻度方向、网格线时,逐一手动设置是不可接受的。matplotlib提供了多种批量操作方法。

方法一:循环遍历Axes数组。这是最直接的方法。

fig, axs = plt.subplots(4, 5, figsize=(15, 12)) for ax in axs.flat: # 使用 .flat 进行一维迭代 ax.set_xlabel('Time (s)', fontsize=9) ax.tick_params(axis='both', labelsize=8) ax.grid(True, linestyle=':', alpha=0.6)

方法二:使用plt.rcParams设置全局参数。这会影响本脚本之后创建的所有图表。

import matplotlib as mpl mpl.rcParams['axes.titlesize'] = 10 mpl.rcParams['axes.labelsize'] = 9 mpl.rcParams['xtick.labelsize'] = 8 mpl.rcParams['ytick.labelsize'] = 8 # 然后再创建子图,所有子图都会应用这些样式

方法三:使用样式表(Style Sheet)。matplotlib内置了多种样式(如‘ggplot’,‘seaborn’,‘fivethirtyeight’),一行代码就能切换整体风格。

plt.style.use('seaborn-v0_8-darkgrid') # 使用 seaborn 的深色网格样式 fig, axs = plt.subplots(2, 2) # 所有子图自动具有 seaborn 风格

注意rcParams和样式表的设置是全局的,可能会影响图中其他你不希望改变的元素,或者影响同一会话中后续的图表。在函数或局部作用域内,可以使用with mpl.rc_context():来临时修改参数,避免污染全局环境。

3.3 色彩映射(Colormap)在并列图中的应用

热词中提到了matplotlib colormap。在并列图中,色彩映射常用于两种场景:

  1. 多个子图表示同一指标在不同条件下的值,使用统一的colormap和归一化(Normalize)来保证颜色代表的数值意义一致。
  2. 在一个子图内部,用颜色表示第三个维度(如散点图的点密度、线图的序列)。

对于场景一,关键在于创建统一的Normalize实例。

import matplotlib.cm as cm import matplotlib.colors as mcolors data_list = [np.random.rand(10,10) for _ in range(6)] # 6个矩阵 vmin, vmax = min(d.min() for d in data_list), max(d.max() for d in data_list) # 全局最小最大值 norm = mcolors.Normalize(vmin=vmin, vmax=vmax) # 统一的归一化器 cmap = cm.viridis # 选择一个colormap fig, axs = plt.subplots(2, 3, figsize=(12, 8)) for ax, data in zip(axs.flat, data_list): im = ax.imshow(data, norm=norm, cmap=cmap) # 应用相同的 norm 和 cmap ax.set_axis_off() # 为整个图添加一个共享的颜色条 fig.colorbar(im, ax=axs.ravel().tolist(), orientation='horizontal', fraction=0.02, pad=0.1)

这样,所有子图中的颜色都基于同一套标尺,对比才有效。

4. 实战:构建一个复杂的多排多列分析仪表板

让我们综合运用以上知识,构建一个模拟的数据分析仪表板。假设我们有一组时间序列数据(例如,来自多个传感器的读数),我们需要从多个视角可视化它。

场景:我们有3个传感器(A, B, C)过去100个时间点的读数。我们要创建一个2行2列的仪表板:

  1. 左上角(主图):三个传感器的时间序列折线图,带图例。
  2. 右上角:三个传感器读数的联合分布散点图矩阵(这里简化为A vs B, A vs C, B vs C的散点图,实际可用seaborn.pairplot更简单)。
  3. 左下角:每个传感器读数的直方图,查看分布。
  4. 右下角:移动平均线(窗口=10)后的时间序列,观察趋势。

我们将使用GridSpec来实现,因为四个区域的尺寸和比例可能不同。

import numpy as np import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec # 1. 生成模拟数据 np.random.seed(42) time = np.arange(100) # 生成有相关性的传感器数据 sensor_A = np.cumsum(np.random.randn(100)) + 20 sensor_B = 0.8 * sensor_A + np.random.randn(100) * 2 + 5 sensor_C = 0.5 * sensor_A + 0.3 * sensor_B + np.random.randn(100) * 1.5 data_dict = {'A': sensor_A, 'B': sensor_B, 'C': sensor_C} colors = {'A': 'tab:blue', 'B': 'tab:orange', 'C': 'tab:green'} # 2. 创建画布和GridSpec布局 # 我们希望主图(时间序列)宽一些,分布图高一些 fig = plt.figure(figsize=(14, 10)) gs = gridspec.GridSpec(2, 2, figure=fig, width_ratios=[3, 2], height_ratios=[2, 1.5], hspace=0.3, wspace=0.3) # 控制子图间距 # 3. 左上角:多线时间序列图 ax_main = fig.add_subplot(gs[0, 0]) for key, color in colors.items(): ax_main.plot(time, data_dict[key], label=f'Sensor {key}', color=color, linewidth=1.5) ax_main.set_xlabel('Time Step') ax_main.set_ylabel('Reading') ax_main.set_title('Raw Time Series of Three Sensors') ax_main.legend(loc='upper left', fontsize=9) ax_main.grid(True, alpha=0.3) # 4. 右上角:散点图矩阵 (简化版) # 我们手动创建三个散点图小网格,这可以用subplots_mosaic或GridSpec嵌套更优雅,这里为演示清晰手动定位 gs_scatter = gs[0, 1].subgridspec(2, 2, wspace=0.1, hspace=0.1) # 在右上角单元格内再建一个2x2网格 ax_scatter_ab = fig.add_subplot(gs_scatter[0, 0]) ax_scatter_ac = fig.add_subplot(gs_scatter[0, 1]) ax_scatter_bc = fig.add_subplot(gs_scatter[1, 0]) # 隐藏右下角,可以用于放图例或其他 ax_corner = fig.add_subplot(gs_scatter[1, 1]) ax_corner.set_axis_off() scatter_pairs = [(ax_scatter_ab, 'A', 'B'), (ax_scatter_ac, 'A', 'C'), (ax_scatter_bc, 'B', 'C')] for ax, x_key, y_key in scatter_pairs: ax.scatter(data_dict[x_key], data_dict[y_key], s=10, alpha=0.6, c=colors[x_key]) ax.set_xlabel(x_key, fontsize=9) ax.set_ylabel(y_key, fontsize=9) ax.tick_params(labelsize=8) # 添加相关系数文本 corr = np.corrcoef(data_dict[x_key], data_dict[y_key])[0, 1] ax.text(0.05, 0.95, f'ρ={corr:.2f}', transform=ax.transAxes, fontsize=8, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5)) # 5. 左下角:并排直方图 ax_hist = fig.add_subplot(gs[1, 0]) bin_edges = np.linspace(min(min(data_dict.values())), max(max(data_dict.values())), 30) for key, color in colors.items(): ax_hist.hist(data_dict[key], bins=bin_edges, alpha=0.6, label=key, color=color, density=True) ax_hist.set_xlabel('Reading Value') ax_hist.set_ylabel('Density') ax_hist.set_title('Distribution of Sensor Readings') ax_hist.legend() ax_hist.grid(True, alpha=0.3) # 6. 右下角:移动平均趋势图 ax_ma = fig.add_subplot(gs[1, 1]) window = 10 for key, color in colors.items(): data = data_dict[key] # 简单移动平均 weights = np.ones(window) / window data_ma = np.convolve(data, weights, mode='valid') ax_ma.plot(time[window-1:], data_ma, label=f'{key} (MA{window})', color=color, linestyle='--') ax_ma.set_xlabel('Time Step') ax_ma.set_ylabel('Smoothed Reading') ax_ma.set_title(f'Moving Average (window={window})') ax_ma.legend(loc='lower right', fontsize=9) ax_ma.grid(True, alpha=0.3) # 7. 自动调整布局 plt.suptitle('Multi-Sensor Data Analysis Dashboard', fontsize=16, y=0.98) plt.tight_layout() # 注意:如果用了constrained_layout,这里可以不用。这里我们用tight_layout做最后微调。 plt.show()

这个例子展示了如何将GridSpec、子图嵌套、循环批量操作、样式统一等技巧融合在一个复杂的图表中。关键在于提前规划布局,并用GridSpec将布局意图转化为代码。

5. 常见问题与排查技巧实录

在实际操作中,你一定会遇到各种布局“翻车”现场。这里记录了几个最常见的问题和我的解决方案。

5.1 子图重叠或标签被裁剪

症状:图表保存为图片后,子图标题、坐标轴标签挤在一起,甚至被切掉。根因:画布(figsize)太小,或者子图之间的间距(hspace,wspace)设置不足,而元素(标题、标签、刻度)又太多太大。解决方案

  1. 优先使用constrained_layout=Trueplt.tight_layout()。在创建Figure或调用plt.subplots时加上constrained_layout=True,它能自动处理大部分间距问题。如果已经画完,调用plt.tight_layout()也能起到类似效果,但有时需要调整pad参数(如plt.tight_layout(pad=2.0))。
  2. 增大figsize。这是根本方法。给画布更多空间。
  3. 调整子图间距。在GridSpecplt.subplots_adjust中调整hspace(高度间距)和wspace(宽度间距)。例如:plt.subplots_adjust(left=0.1, right=0.95, bottom=0.1, top=0.9, wspace=0.3, hspace=0.4)。数值是相对于画布尺寸的比例。
  4. 减小字体和标记大小。通过rcParams或对每个Axes设置tick_paramstitlefontsize

5.2 共享坐标轴后刻度标签消失

症状:设置了sharex=True后,只有最下面一行子图有x轴标签,中间行的x轴刻度标签不见了。预期行为:这正是共享坐标轴的目的,避免重复标签,让图表更简洁。如果你需要显示:可以通过ax.tick_params(labelbottom=True)强制显示某个特定子图的底部刻度标签。但通常不建议这样做,破坏了整洁性。更好的做法是,如果所有子图的x轴数据相同,共享轴并隐藏冗余标签是最佳实践。

5.3 循环中无法正确索引子图数组

症状:当子图网格是2维数组时(如axs = plt.subplots(2,3)返回的axs(2,3)数组),直接用for ax in axs:循环,ax会是行向量,而不是一个个的Axes对象。正确做法:使用axs.flat进行一维迭代,或者使用axs.ravel()。如果需要行列索引,可以用for i in range(nrows): for j in range(ncols): ax = axs[i, j]

fig, axs = plt.subplots(2, 3) # 错误:ax是包含3个Axes的数组 for ax in axs: print(type(ax)) # <class 'numpy.ndarray'> # 正确:ax是单个Axes对象 for ax in axs.flat: print(type(ax)) # <class 'matplotlib.axes._subplots.AxesSubplot'>

5.4 颜色条(Colorbar)位置错乱或影响布局

症状:为多个子图(如imshow图)添加一个共享的颜色条时,颜色条可能会挤压子图的空间,导致布局变形。解决方案:使用fig.colorbar()ax参数传入一个Axes列表,并利用fractionpad参数精细控制颜色条的大小和位置。

import numpy as np import matplotlib.pyplot as plt fig, axs = plt.subplots(2, 2, constrained_layout=True) im_list = [] for ax in axs.flat: data = np.random.rand(10, 10) im = ax.imshow(data) im_list.append(im) ax.set_axis_off() # 为所有子图添加一个水平颜色条,放在底部 cbar = fig.colorbar(im_list[0], ax=axs.ravel().tolist(), orientation='horizontal', fraction=0.05, # 颜色条占Axes高度的比例 pad=0.05) # 颜色条与Axes的间距 cbar.set_label('Intensity')

使用constrained_layout=True能更好地协调颜色条和子图之间的空间。

5.5 保存的图片分辨率不足或尺寸不对

症状:屏幕上显示很好,保存为PNG或PDF后模糊,或者图形元素相对位置变了。解决方案

  1. 指定DPI:在plt.savefig()时使用dpi参数。用于出版的图片通常需要300 dpi或更高。plt.savefig('output.png', dpi=300, bbox_inches='tight')
  2. 使用bbox_inches='tight':这个参数会自动裁剪图片周围的空白区域,确保所有内容都被包含。但要注意,它可能会轻微改变图形的相对尺寸。如果对布局有极致要求,可能需要手动调整figsize和边距,而不用tight
  3. 保存为矢量格式:对于论文发表,优先保存为PDF或SVG格式。它们是矢量图,无限放大不失真。plt.savefig('output.pdf')
  4. 在显示前保存:有时plt.show()会重置一些图形属性。安全的做法是在plt.show()之前调用plt.savefig()

6. 进阶探索:从静态网格到动态交互

matplotlib不仅限于静态图。结合一些技巧,我们可以让多子图布局产生联动效果,提升探索性数据分析的体验。

6.1 跨子图刷选与高亮

一个常见的需求是:在一个散点图矩阵中,选中一些点,在其他子图(如时间序列图)中高亮显示这些点对应的数据。这可以通过matplotlib的事件处理系统(mpl_connect)来实现。核心思路是:

  1. 在所有相关的Axes对象中绘制数据时,保留数据点的引用(如Line2DPathCollection对象)。
  2. 在一个子图上添加选取事件(如pick_event)的监听器。
  3. 当事件触发时,根据选取点的索引,更新其他子图中对应数据点的属性(如颜色、大小、zorder)。

这需要较多的代码,但能构建出强大的交互式仪表板。对于更复杂的交互,可以考虑PlotlyBokehipywidgets等库。

6.2 与NumPy结合:计算“子图邻居”

热词中提到了“numpy 计算matplotlib画的方块邻居元素之和”。这启发了一个有趣的应用:将子图阵列本身视为一个数据网格。例如,我们画一个MxNimshow子图阵列,每个子图显示一个随机矩阵。然后,我们想计算每个“方块”(子图)与其上下左右邻居(在子图网格意义上)所显示矩阵的某种聚合(如和、均值)。

这更像是一个编程思维练习,但实现起来很清晰:

  1. plt.subplots(M, N)创建网格,axs是一个MxN数组。
  2. 为每个axs[i, j]imshow画一个随机矩阵data[i, j](这里data是一个三维数组(M, N, H, W))。
  3. 使用numpy的切片操作,计算每个位置(i,j)的邻居索引(注意边界处理),然后对data进行相应的聚合计算。
  4. 可以将计算结果再画到一个新的(M, N)网格中,形成对比。
M, N = 3, 4 H, W = 5, 5 # 生成随机数据网格 data_grid = np.random.rand(M, N, H, W) fig, axs = plt.subplots(M, N, figsize=(N*2, M*2)) for i in range(M): for j in range(N): axs[i, j].imshow(data_grid[i, j], cmap='viridis') axs[i, j].set_axis_off() axs[i, j].set_title(f'({i},{j})') # 计算每个“方块”与其四邻居(上、下、左、右)数据之和 neighbor_sum_grid = np.zeros((M, N, H, W)) for i in range(M): for j in range(N): neighbor_sum = np.zeros((H, W)) # 上邻居 if i > 0: neighbor_sum += data_grid[i-1, j] # 下邻居 if i < M-1: neighbor_sum += data_grid[i+1, j] # 左邻居 if j > 0: neighbor_sum += data_grid[i, j-1] # 右邻居 if j < N-1: neighbor_sum += data_grid[i, j+1] neighbor_sum_grid[i, j] = neighbor_sum # 可视化邻居和 fig2, axs2 = plt.subplots(M, N, figsize=(N*2, M*2)) for i in range(M): for j in range(N): axs2[i, j].imshow(neighbor_sum_grid[i, j], cmap='plasma') axs2[i, j].set_axis_off() axs2[i, j].set_title(f'Sum of Neighbors\nof ({i},{j})') plt.tight_layout() plt.show()

这个例子展示了如何将matplotlib的子图布局与numpy的数组计算无缝结合,实现数据与可视化结构上的同步操作。

画好多排多列的并列图,核心在于对matplotlib布局系统的深入理解和对项目需求的提前规划。从简单的plt.subplots开始,遇到复杂布局时果断升级到GridSpec,善用constrained_layout管理空间,通过循环和样式表提升效率,最后用高DPI保存成果。记住,好的可视化不仅是把图画出来,更是让信息清晰、高效地传递。每次画图前,花一分钟在纸上草图一下布局,往往能节省后面大量的调试时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询