如何在Matplotlib中单独控制子图尺寸,并解决GridSpec布局下的行间距过大问题?
如何在Matplotlib中单独控制子图尺寸,并解决GridSpec布局下的行间距过大问题?
我完全懂你的困扰——把带地理投影的imshow子图和时序线图放在一起时,要么地图子图因为投影比例限制显得过小、布局不协调;要么用GridSpec调整布局后,两行子图之间的空白间距大得没法看。咱们来一步步解决这两个问题:
首先,理解最初的子图大小问题
你用普通plt.subplot()时,带ccrs.PlateCarree()投影的子图默认会强制等比例缩放(aspect='equal'),目的是保证地图不会被拉伸变形,但这就导致它在和旁边没有比例限制的时序图对比时,看起来尺寸偏小。这是地理投影子图的默认特性,不是bug。
解决GridSpec行间距过大的核心问题
你用GridSpec的思路是对的,但问题出在两个地方:
- 你同时用了
plt.tight_layout()和plt.subplots_adjust(),这两个函数会互相冲突——tight_layout()会自动覆盖手动设置的间距参数; - 你的GridSpec行数设置(6行)和高度比例分配,导致中间出现了不必要的空白区域,再加上tight_layout的自动调整,就形成了巨大的行间距。
修正后的解决方案
下面是调整后的代码,既保证地图子图的合适尺寸,又能消除多余的行间距:
import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec import cartopy.crs as ccrs import pandas as pd # 假设titles、eofs、pcs这些变量已经定义好 # 创建figure时启用constrained_layout,它比tight_layout更智能,能自动处理间距 fig = plt.figure(figsize=(15, 9), constrained_layout=True) # 重新定义GridSpec:2行4列,宽度比例控制时序图的宽度,高度比例保持每行一致 gs = gridspec.GridSpec(2, 4, width_ratios=[1, 0.7, 1, 0.7], height_ratios=[1, 1]) # 地图子图:放在每行的第0、2列位置 map_subplots = [ plt.subplot(gs[0, 0], projection=ccrs.PlateCarree()), plt.subplot(gs[0, 2], projection=ccrs.PlateCarree()), plt.subplot(gs[1, 0], projection=ccrs.PlateCarree()), plt.subplot(gs[1, 2], projection=ccrs.PlateCarree()) ] for i, ax in enumerate(map_subplots): ax.set_title(titles[i]) ax.coastlines(linewidth=0.5) ax.imshow(eofs[i], extent=[0, 360, -90, 90], transform=ccrs.PlateCarree(), cmap='RdBu_r', origin='lower', vmin=-0.01, vmax=0.01) # 时序子图:放在每行的第1、3列位置,和对应地图子图同一行 ts_subplots = [ plt.subplot(gs[0, 1]), plt.subplot(gs[0, 3]), plt.subplot(gs[1, 1]), plt.subplot(gs[1, 3]) ] for i, ax in enumerate(ts_subplots): ax.plot(pcs.time, pcs.sel(pc=i)) ax.set_ylim(-100, 100) ax.set_yticks([]) # 直接去掉yticks,简化冗余判断 ax.set_xticks(pd.date_range("2013-01-01", "2016-01-01", freq='6MS')) ax.set_xticklabels(["2013", "Jul", "2014", "Jul", "2015", "Jul", "2016"]) # 如果需要添加公共y轴标签,用fig.text更合适 # fig.text(0.04, 0.5, 'PC value (arb. units)', va='center', rotation='vertical', fontsize=12) plt.show()
关键调整点说明
- 启用
constrained_layout=True:在创建figure时设置这个参数,它会自动计算子图之间的合理间距,不需要再用tight_layout()或subplots_adjust(),避免了参数冲突; - 简化GridSpec结构:把原来的6行改成2行,让每个地图子图和对应的时序图在同一行,从根源上消除了中间的空白行;
- 宽度比例精准控制:
width_ratios=[1, 0.7, 1, 0.7]让地图子图宽度为1,时序图为0.7,保持你想要的比例关系; - 去掉冗余代码:删掉了不必要的
if True判断,让代码更简洁。
如果还是需要更精细的间距调整,可以用gs.update(wspace=0.1, hspace=0)来手动微调,constrained_layout会尊重这些设置。
备注:内容来源于stack exchange,提问作者J.Barker




