Matplotlib多子图热力图固定位置添加黑色方块标记及文本方法
问题根源
代码中补丁和文本无法正常显示,核心是以下几个问题:
- 坐标体系不匹配:给
imshow传入extent参数后,坐标轴范围被设置为百万级的真实地理坐标,但添加矩形、文本时用的是0~149的网格索引坐标,二者差了6个数量级,元素直接跑到可视范围外 - 子图对象调用错误:循环里用
plt.gca()获取子图,该方法只会返回最后一个激活的子图,不是当前遍历到的子图,就算坐标正确,元素也只会出现在最后一个子图上 - 矩形参数错误:需要覆盖x、y方向60~64共4个网格单元,但传入的矩形宽高为3,且Rectangle第一个参数为左下角坐标,原参数计算出的覆盖范围不符合预期
- 文本添加逻辑错误:写了两次文本添加代码,第一次错把网格的数值当坐标传入,第二次错误把text对象当成patch传给
add_patch,根本不会生效;另外黑方块配黑字就算显示出来也无法识别 - 语法问题:用Python关键字
property当变量名,且layer、reservoir_property两个变量未定义,运行会直接报错
修复后完整代码
from matplotlib.patches import Rectangle import numpy as np import matplotlib.pyplot as plt import os import matplotlib.colors as mcolors # 模拟数据 data = np.random.rand(10, 150, 150) data = data.reshape(-1, 1) # 修改变量名避免关键字冲突 reservoir_property = "Sale" layer = 1 pmin = data.min() pmax = data.max() v = np.linspace(round(pmin,3), round(pmax,3),15, endpoint=True) v = [round(x,3) for x in v] # 原figsize(160,80)过大,测试时可缩小,需要出大图可改回原值 fig, ax = plt.subplots(2, 5, figsize=(16, 8)) row_count = 0 col_count = 0 # 地理边界参数 xmin = 1258096.6 xmax = 1291155.0 ymin = 11251941.6 ymax = 11285000.0 grid_num = 150 # 计算单个网格对应的地理坐标长度 x_grid_step = (xmax - xmin) / grid_num y_grid_step = (ymax - ymin) / grid_num # 计算标记方块的地理坐标:左下角(60,60)网格对应位置,宽高对应4个网格 rect_x0 = xmin + 60 * x_grid_step rect_y0 = ymin + 60 * y_grid_step rect_w = 4 * x_grid_step rect_h = 4 * y_grid_step # 计算文本中心位置:(62,62)网格对应坐标 text_x = xmin + 62 * x_grid_step text_y = ymin + 62 * y_grid_step for i in range(10): sub_plot_data = data[(i)*(150*150):(i+1)*150*150] pmin_sub = sub_plot_data.min() pmax_sub = sub_plot_data.max() # 定义离散色条 bounds = np.linspace(-1, 1, 10) Discrete_colors = plt.get_cmap('jet')(np.linspace(0,1,len(bounds)+1)) cmap = mcolors.ListedColormap(Discrete_colors[1:-1]) actual_2d = np.reshape(sub_plot_data,(grid_num,grid_num)) current_ax = ax[row_count, col_count] # 加origin='lower'让y轴方向和网格索引一致,若数据y轴从上到下递增可删除该参数,同时调整y坐标计算逻辑即可 im1 = current_ax.imshow(actual_2d, interpolation=None, cmap=cmap, extent=(xmin, xmax, ymin, ymax), vmin=pmin_sub, vmax=pmax_sub, origin='lower') # 直接给当前子图加黑色方块 current_ax.add_patch(Rectangle((rect_x0, rect_y0), rect_w, rect_h, edgecolor='black', facecolor='black', fill=True, lw=2)) # 加白色文本,黑底白字保证可见,原fontsize=90对应超大figsize,小尺寸下对应调整即可 current_ax.text(text_x, text_y, 'Sale 1', horizontalalignment='center', verticalalignment='center', color='white', fontsize=12) current_ax.set_title("Sale_Stores-%s - L: %s"%(i+1, layer), fontsize=13, pad=4, x=0.5, y=0.999) current_ax.set_aspect('auto') current_ax.tick_params(left=False, labelleft=False, top=False, labeltop=False, right=False, labelright=False, bottom=False, labelbottom=False) col_count +=1 if col_count == 5: row_count +=1 col_count =0 fig.tight_layout(h_pad=1) plt.subplots_adjust(left=0.02, bottom=0.1, right=0.91, top=0.8, wspace=0.1, hspace=0.2) cbaxes = fig.add_axes([0.94, 0.05, 0.02, 0.8]) cbar = fig.colorbar(im1, ax=ax.ravel().tolist(), ticks=v, extend='both', cax=cbaxes) cbar.ax.tick_params(labelsize=7) cbar.ax.set_yticklabels([i for i in v], fontsize=12) # 保存部分 output_dir = r"D/test" plot_dir = os.path.join(output_dir, reservoir_property) if not os.path.exists(plot_dir): os.makedirs(plot_dir) fig.savefig(r"%s/per_allmodel.png"%(plot_dir))
关键修改说明
- 所有绘图元素直接绑定当前循环拿到的子图对象
current_ax,不再使用plt.gca(),确保10个子图都能正常添加标记 - 提前将网格索引位置换算成和
extent一致的地理坐标,彻底解决坐标不匹配问题;如果不需要显示地理坐标,直接删掉imshow里的extent参数,矩形和文本直接用网格索引坐标即可,无需换算 - 修正矩形尺寸:4个网格长度对应宽高4,刚好覆盖60~64的范围
- 去掉重复、错误的文本代码,文本颜色改为白色,保证黑块上文字可见
- 清理重复导入的冗余库,补上未定义变量,重命名占用关键字的变量,修复运行报错问题
内容的提问来源于stack exchange,提问作者bluered_earth
相关产品推荐
相关产品推荐

