设置统一y轴范围后,Matplotlib子图图例无法最优放置
问题描述
我用Matplotlib绘制了包含两个子图的图表,想要让它们使用相同的y轴刻度范围,相关代码如下:
fig, ax = plt.subplots(1, 2,figsize=(10,6)) ax[0].set_title('Over Global region, {} points'.format(len(df))) ax[0].plot(df.groupby('type')['thickness'].mean(),label='Cloud mean thickness',color='red') ax[0].set_xlabel('Cloud Type',fontsize=13) ax[0].set_ylabel('(in km)',fontsize=15) #Put xticks with labels at the bottom of the plot ax[0].set_xticks(cloudlabel,cloudtype_dict.values(),rotation=50) lon_left, lon_right, lat_bot, lat_top = 60, 100, 0, 40 dfcopy = df.copy() dfcopy_aoi = dfcopy[(dfcopy['lon']>=lon_left) & (dfcopy['lon']<=lon_right) & (dfcopy['lat']>=lat_bot) & (dfcopy['lat']<=lat_top)] ax[1].set_title('Over Indian subcontinent region, {} points'.format(len(dfcopy_aoi))) ax[1].plot(dfcopy_aoi.groupby('type')['thickness'].mean(),label='Cloud mean thickness',color='red') ax[1].set_xlabel('Cloud Type',fontsize=13) ax[1].set_ylabel('(in km)',fontsize=15) ax[1].set_xticks(cloudlabel,cloudtype_dict.values(),rotation=50); #Shared y-axis y_lim=[0,0] y_lim = [min(ax[0].get_ylim()[0], ax[1].get_ylim()[0]), max(ax[0].get_ylim()[1], ax[1].get_ylim()[1])] for i in range(2): ax[i].set_ylim(y_lim) ax[0].legend(loc='best'); ax[1].legend(loc='best');
我已经把图例设置代码放在所有操作之后,但第一个子图(ax[0])的图例还是错位,没有找到最优放置位置,图例跑到了子图外部区域。
解决方案
核心原因
问题出在:你先绘制图表,Matplotlib自动算出初始y轴范围并记录了绘图区域;之后手动修改了y轴范围,但调用legend(loc='best')时,它还是基于修改y轴范围之前的区域计算最优位置,导致图例错位。
修复方案
有两种简单解决办法:
- 先确定统一y轴范围,再绘制图表
先提前计算两组数据的均值,得出统一的y轴上下限,再依次绘制子图:
# 提前计算两组数据的均值,确定统一y轴范围 global_mean = df.groupby('type')['thickness'].mean() aoi_mean = dfcopy_aoi.groupby('type')['thickness'].mean() y_lim = [min(global_mean.min(), aoi_mean.min()), max(global_mean.max(), aoi_mean.max())] fig, ax = plt.subplots(1, 2,figsize=(10,6)) # 绘制第一个子图 ax[0].set_title(f'Over Global region, {len(df)} points') ax[0].plot(global_mean, label='Cloud mean thickness', color='red') ax[0].set_xlabel('Cloud Type', fontsize=13) ax[0].set_ylabel('(in km)', fontsize=15) ax[0].set_xticks(cloudlabel, cloudtype_dict.values(), rotation=50) ax[0].set_ylim(y_lim) ax[0].legend(loc='best') # 绘制第二个子图 ax[1].set_title(f'Over Indian subcontinent region, {len(dfcopy_aoi)} points') ax[1].plot(aoi_mean, label='Cloud mean thickness', color='red') ax[1].set_xlabel('Cloud Type', fontsize=13) ax[1].set_ylabel('(in km)', fontsize=15) ax[1].set_xticks(cloudlabel, cloudtype_dict.values(), rotation=50) ax[1].set_ylim(y_lim) ax[1].legend(loc='best')
- 修改y轴范围后,强制更新图例位置计算
如果不想调整代码顺序,可以在设置完y轴范围后,给legend加上bbox_transform参数,让它基于当前子图的轴区域计算位置:
# 设置完y轴范围后 for i in range(2): ax[i].set_ylim(y_lim) # 重新基于当前轴区域计算图例位置 ax[0].legend(loc='best', bbox_transform=ax[0].transAxes) ax[1].legend(loc='best', bbox_transform=ax[1].transAxes)
或者直接手动指定一个可靠的位置(比如upper right),避免自动计算的误差:
ax[0].legend(loc='upper right') ax[1].legend(loc='upper right')
额外优化建议
可以直接用Matplotlib自带的共享轴功能,不用手动计算y范围,代码更简洁:
# 创建子图时指定sharey=True,自动同步两个子图的y轴范围 fig, ax = plt.subplots(1, 2, figsize=(10,6), sharey=True) # 后续绘制代码无需再手动设置ylim
内容的提问来源于stack exchange,提问作者Pixel_Bear
相关产品推荐
相关产品推荐

