Matplotlib共享y轴时设置单图等纵横比并保持轴尺寸对齐
Matplotlib双列子图等纵横比+Y轴对齐实现方案
问题背景
需要实现左右排布的双列子图:
- 左侧子图展示二维剖面数据,要求坐标轴等纵横比(即X/Y方向单位公里的物理显示长度一致)
- 右侧子图展示二维数据指定位置的垂直切片,两个子图共享Y轴(深度轴),要求深度轴完全对齐
初始实现的最小工作代码如下:
import matplotlib.pyplot as plt import numpy as np nz = 66 nx = 130 ny = 230 h = 70 ensemble_size = 50 x = np.array([ix*h / 1000 for ix in range(nx)]) y = np.array([iy*h / 1000 for iy in range(ny)]) z = np.array([iz*h / 1000 for iz in range(nz)]) Y, Z = np.meshgrid(y, z, indexing='ij') vp_min = 1400 vp_max = 3350 p_max = 1200 ix = 42 iy = 81 vp = (vp_max - vp_min)*np.random.rand(nz*nx*ny, ensemble_size) + vp_min vp_mean = np.mean(vp, axis=1).reshape(nz*nx*ny, 1) ######################################################################## fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True) ax1.pcolor(Y, Z, vp_mean.reshape(nz, nx, ny, order='F')[:,ix,:].T, cmap="jet", vmin=vp_min, vmax=vp_max) ax1.plot(iy*h*np.ones(nz) / 1000, z, "k--") ax1.set_ylabel(r'Depth ($km$)') ax1.set_xlabel(r'Y ($km$)') ax1.set_aspect('equal') lines = [] for e in range(ensemble_size): lines.append( ax2.plot(vp[:,e].reshape(nz, nx, ny, order='F')[:,ix,iy], z, "b", alpha=0.25, label="models") ) lines.append( ax2.plot(vp_mean.reshape(nz, nx, ny, order='F')[:,ix,iy], z, "r", alpha=1.0, label="average model") ) plt.setp(lines[1:ensemble_size], label="_") ax2.set_xlabel(r'$V_p$ ($m/s$)') ax2.invert_yaxis() ax2.legend() ######################################################################## plt.savefig("log_" + str(ix) + "_" + str(iy) + ".pdf", bbox_inches='tight') plt.show(block=False) plt.close("all")
现存问题
初始版本左侧子图纵横比显示不协调,尝试通过gridspec_kw手动指定子图宽度比例,同时保留左侧子图等纵横比设置,核心修改如下:
fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True, gridspec_kw={'width_ratios':[5, 1]}) # 其余代码不变 ax1.set_aspect(1)
该修改会导致两个子图的深度(Y轴)物理尺寸不对齐,无法满足布局要求。
问题原因
set_aspect('equal')会强制轴的物理尺寸匹配数据坐标的比例,该逻辑会覆盖gridspec_kw预设的子图尺寸规则,自动调整左侧子图的高度,最终和右侧子图的高度产生偏差,即便设置了sharey=True也无法对齐。
解决方案
使用mpl_toolkits.axes_grid1提供的HBoxDivider工具手动控制两个子图的尺寸分配,强制两个子图高度一致,同时保留左侧子图的等纵横比设置,支持自定义两个子图的宽度比例。
修改后完整代码
import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.axes_grid1.axes_divider import HBoxDivider import mpl_toolkits.axes_grid1.axes_size as Size nz = 66 nx = 130 ny = 230 h = 70 ensemble_size = 50 x = np.array([ix*h / 1000 for ix in range(nx)]) y = np.array([iy*h / 1000 for iy in range(ny)]) z = np.array([iz*h / 1000 for iz in range(nz)]) Y, Z = np.meshgrid(y, z, indexing='ij') vp_min = 1400 vp_max = 3350 p_max = 1200 ix = 42 iy = 81 vp = (vp_max - vp_min)*np.random.rand(nz*nx*ny, ensemble_size) + vp_min vp_mean = np.mean(vp, axis=1).reshape(nz*nx*ny, 1) ######################################################################## # 初始化画布,不提前设置gridspec_kw fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True) # 左侧子图绘图 ax1.pcolor(Y, Z, vp_mean.reshape(nz, nx, ny, order='F')[:,ix,:].T, cmap="jet", vmin=vp_min, vmax=vp_max) ax1.plot(iy*h*np.ones(nz) / 1000, z, "k--") ax1.set_ylabel(r'Depth ($km$)') ax1.set_xlabel(r'Y ($km$)') ax1.set_aspect('equal') # 保留等纵横比设置 ax1.invert_yaxis() # 深度轴反转统一放到左侧设置,共享轴会自动同步 # 右侧子图绘图 lines = [] for e in range(ensemble_size): lines.append( ax2.plot(vp[:,e].reshape(nz, nx, ny, order='F')[:,ix,iy], z, "b", alpha=0.25, label="models") ) lines.append( ax2.plot(vp_mean.reshape(nz, nx, ny, order='F')[:,ix,iy], z, "r", alpha=1.0, label="average model") ) plt.setp(lines[1:ensemble_size], label="_") ax2.set_xlabel(r'$V_p$ ($m/s$)') ax2.legend() # 手动设置子图尺寸分配,强制Y轴对齐 pad = 0.6 # 两个子图之间的间距,可根据需求调整 divider = HBoxDivider( fig, 111, horizontal=[Size.AxesX(ax1), Size.Fixed(pad), Size.AxesX(ax2)], vertical=[Size.AxesY(ax1), Size.Scaled(1), Size.AxesY(ax2)] ) ax1.set_axes_locator(divider.new_locator(0)) ax2.set_axes_locator(divider.new_locator(2)) # 如需调整左右宽度比例,可手动修改右侧子图x轴范围实现,例: # ax2.set_xlim(vp_min-100, vp_max+100) ######################################################################## plt.savefig("log_" + str(ix) + "_" + str(iy) + ".pdf", bbox_inches='tight') plt.show(block=False) plt.close("all")
效果说明
- 左侧子图保留1:1等纵横比,X/Y方向单位公里的显示长度完全一致
- 两个子图深度轴物理尺寸完全对齐,无高度偏差
- 可通过调整右侧子图X轴范围、子图间距参数自定义布局比例
内容的提问来源于stack exchange,提问作者Alexandre Hoffmann
相关产品推荐
相关产品推荐

