Matplotlib使用GridSpec布局时指定轴绘制colorbar占空间过多如何解决?
问题解决方法
你遇到的colorbar超出GridSpec分配范围的问题,核心由三个错误导致,对应修改方案如下:
错误原因
- GridSpec参数顺序写反:
plt.GridSpec()第一个参数是总行数,第二个是总列数,你原代码传入的width、height顺序颠倒,初始布局就不符合预期 - 行高分配不合理:GridSpec默认所有行高度均等,但你给网格子图、图例子图都设置了
set_aspect(1.0)强制正方形,渲染时这两个子图会自动压缩高度,多出来的垂直空间就被没有设置等比例的colorbar轴占用,导致colorbar看起来越界 - 画布高度计算未考虑图例、colorbar的额外高度需求,整体垂直空间不足
修正代码
你只需要修改main函数里的GridSpec定义、画布尺寸计算部分即可,以下是完整可运行的修正后代码:
from matplotlib import pyplot as plt import matplotlib.cm as matplotlib_cm import numpy as np import matplotlib as mpl import os def plot_legend_in_given_axis(ax, fontsize): diameter_labels = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] row_labels = ['circle'] ax.grid(which="major", color="white", linestyle='-', linewidth=3) ax.set_aspect(1.0) for row_index, row_label in enumerate(row_labels): for diameter_index, diameter_label in enumerate(diameter_labels): circle=plt.Circle((diameter_index + 0.5, row_index + 0.5), radius=(diameter_label/(2*1.09)), color='gray', fill=True) ax.add_artist(circle) ax.set_xlim([0, len(diameter_labels)]) ax.set_xticklabels([]) ax.tick_params(axis='x', which='minor', length=0, labelsize=12) ax.set_xticks(np.arange(0, len(diameter_labels), 1)) ax.xaxis.set_ticks_position('bottom') ax.set_xlabel('Very long\nx axis label', fontsize=fontsize, labelpad=5) ax.set_ylim([0, len(row_labels)]) ax.set_yticklabels([]) ax.tick_params(axis='y', which='minor', length=0, labelsize=12) ax.set_yticks(np.arange(0, len(row_labels), 1)) ax.grid(which='major', color='black', linestyle='-', linewidth=1) def main(width, height, xaxis_labels, yaxis_labels): width_multiply = 1.5 height_multiply = 1.5 # 额外增加2行的高度:1行图例 + 1行colorbar figure_width = int(width_multiply * width) figure_height = int(height_multiply * (height + 2)) fig = plt.figure(figsize=(figure_width, figure_height)) # 修正GridSpec参数:总行数=height+2(网格区height行 + 图例1行 + colorbar1行),总列数=width # 用height_ratios指定行高比例,给colorbar和图例分配更小的高度,避免占用过多空间 grid = plt.GridSpec(nrows=height + 2, ncols=width, height_ratios=[1]*height + [0.5, 0.5], hspace=0.3, wspace=0) ax = fig.add_subplot(grid[0:-2, :]) ax.set_aspect(1.0) legend_ax = fig.add_subplot(grid[-2, :]) plot_legend_in_given_axis(legend_ax, 20) color_bar_ax = fig.add_subplot(grid[-1, :]) cmap = matplotlib_cm.get_cmap('YlOrRd') v_min = 2 v_max = 20 norm = plt.Normalize(v_min, v_max) bounds = np.arange(v_min, v_max + 1, 2) cb = mpl.colorbar.ColorbarBase(color_bar_ax, cmap=cmap, norm=norm, ticks=bounds, spacing='proportional', orientation='horizontal') cb.ax.tick_params(labelsize=20) cb.set_label("-log10 (q-value)", horizontalalignment='center', rotation=0, fontsize=20) ax.set_xlim([0, width]) ax.set_xticks(np.arange(0, width + 1, 1)) ax.set_ylim([0, height]) ax.set_yticks(np.arange(0, height + 1, 1)) # Plot the circles with color in the grid for yaxis_index, yaxis_label in enumerate(yaxis_labels): for xaxis_index, xaxis_label in enumerate(xaxis_labels): circle = plt.Circle((xaxis_index + 0.5, yaxis_index + 0.5), 0.4, color=cmap(norm(10)), fill=True) ax.add_artist(circle) ax.grid() for edge, spine in ax.spines.items(): spine.set_visible(True) spine.set_color('black') figures_path = os.path.join('/Users', 'burcakotlu', 'Desktop') figFile = os.path.join(figures_path, 'ColorBar_Using_GridSpec.png') fig.savefig(figFile, dpi=100, bbox_inches="tight") plt.cla() plt.close(fig) width = 5 height = 5 xaxis_labels = ["x%d" %i for i in range(width)] yaxis_labels = ["y%d" %i for i in range(height)] main(width, height, xaxis_labels, yaxis_labels)
关键修改点说明
- 调整了GridSpec的参数顺序,明确指定
nrows为height + 2,预留图例和colorbar的行位置 - 新增
height_ratios参数自定义行高比例,图例和colorbar行高度设为网格行的一半,避免均等分配导致的空间浪费 - 修正了画布高度计算逻辑,额外增加2行的高度空间,避免垂直方向挤压
- 修正了坐标轴范围设置的作用对象,原代码里
plt.xlim、plt.ylim作用到了colorbar轴上,现在明确作用到网格子图ax上
内容的提问来源于stack exchange,提问作者burcak
相关产品推荐
相关产品推荐

