Matplotlib新增子图时报ValueError:图像像素尺寸过大问题求助
matplotlib多子图合并时Image size too large报错排查
问题现象
使用matplotlib的add_subplot函数将多个绘图合并到同一张画布时,抛出Image size pixels is too large错误,单个绘图均可正常生成,合并时触发异常。
复现代码
from matplotlib import pyplot as plt import numpy as np import os def plot_in_subplots(): fig = plt.figure(figsize=(15, 10)) axis1 = fig.add_subplot(311) # 取消下一行注释复现报错 # axis2 = fig.add_subplot(312) plot_bar_plot_in_given_axis(axis1) # plot_in_given_axis(axis2) figFile = os.path.join('/Users/burcakotlu/Desktop/Test_subplot.png') fig.savefig(figFile, dpi=100, bbox_inches="tight") plt.cla() plt.close(fig) def plot_in_given_axis(ax): xticklabels_list = ['a','b','c','d','e','f'] * 6 rows = ['row1'] ax.set_xlim([0, 36]) ax.set_xticklabels([]) ax.tick_params(axis='x', which='minor', length=0, labelsize=35) ax.set_xticks(np.arange(0, 36, 1)) ax.set_xticks(np.arange(0, 36, 1) + 0.5, minor=True) ax.set_xticklabels(xticklabels_list, minor=True) ax.xaxis.set_label_position('top') ax.xaxis.set_ticks_position('top') # 原代码此处为全局plt操作,改为ax调用 ax.tick_params( axis='x', which='major', bottom=False, top=False ) ax.set_ylim([0, len(rows)]) ax.set_yticklabels([]) ax.tick_params(axis='y', which='minor', length=0, labelsize=40) ax.set_yticks(np.arange(0, len(rows), 1)) ax.set_yticks(np.arange(0, len(rows), 1) + 0.5, minor=True) ax.set_yticklabels(rows, minor=True) # 原代码此处为全局plt操作,改为ax调用 ax.tick_params( axis='y', which='major', left=False ) ax.grid(which='major', color='black', zorder=3) def plot_bar_plot_in_given_axis(ax): x_axis_labels = ['a', 'b', 'c', 'd', 'e', 'f'] real_values1 = [266655.0, 0.0, 14072.0, 4137.0, 6752.5, 0.0] real_values2 = [273342.5, 0.0, 12598.5, 4240.0, 7425.5, 0.0] unreal_values1 = [326188.16, 0.0, 15828.42, 4666.825000000001, 8109.87, 0.0] unreal_values2 = [344462.07, 0.0, 16368.664999999999, 5180.2, 8721.64, 0.0] q_values = [2.5309603790195403e-28, 1.0, 1.8194829804783173e-33, 0.003603381046779825, 1.0, 1.0] name1 = 'X' name2 = 'Y' color1 = 'r' color2 = 'b' width = 0.1 ind = np.arange(len(x_axis_labels)) legend = None rects3 = None rects4 = None rects1 = ax.bar(ind, real_values1, width=width, edgecolor='black', color=color1) rects2 = ax.bar(ind + width, real_values2, width=width, edgecolor='black', color=color2) if (unreal_values1 is not None) and unreal_values1: rects3 = ax.bar(ind + 2*width, unreal_values1, width=width, edgecolor='black', color=color1, hatch='X') if (unreal_values2 is not None) and unreal_values2: rects4 = ax.bar(ind + 3*width, unreal_values2, width=width, edgecolor='black', color=color2, hatch='X') ax.tick_params(axis='x', labelsize=35) ax.tick_params(axis='y', labelsize=35) # 原代码此处为全局plt.yticks,改为ax调用 locs = ax.get_yticks() ax.set_ylim(0, locs[-1] + 5000) ax.set_title('%s vs. %s' % (name1, name2), fontsize=20, fontweight='bold') ax.set_xticklabels(x_axis_labels, fontsize=35) # 原代码此处为全局plt.ylabel,改为ax调用 ax.set_ylabel('Y axis label', fontsize=35, fontweight='normal') ax.set_xticks(ind + (3 * width) / 2) realStrand1Name = 'Real %s' % (name1) realStrand2Name = 'Real %s' % (name2) simulationsStrand1Name = 'Unreal %s' % (name1) simulationsStrand2Name = 'Unreal %s' % (name2) if (rects1 is not None) and (rects2 is not None) and (rects3 is not None) and (rects4 is not None): if (len(rects1) > 0) and (len(rects2) > 0) and (len(rects3) > 0) and (len(rects4) > 0): legend = ax.legend((rects1[0], rects2[0], rects3[0], rects4[0]), (realStrand1Name, realStrand2Name, simulationsStrand1Name, simulationsStrand2Name), prop={'size': 25}, ncol=1, loc='best') ax.set_facecolor('white') ax.spines["bottom"].set_color('black') ax.spines["left"].set_color('black') ax.spines["top"].set_color('black') ax.spines["right"].set_color('black') if legend is not None: frame = legend.get_frame() frame.set_facecolor('white') frame.set_edgecolor('black') if q_values is not None: for q_value, rect1, rect2 in zip(q_values, rects1, rects2): y_value = max(rect1.get_height(), rect2.get_height()) x_value = rect1.get_x() + rect1.get_width() space = 3 va = 'bottom' if y_value < 0: space *= -1 va = 'top' if (q_value is not None) and (q_value <= 0.05): # 原代码此处为全局plt.annotate,改为ax调用 ax.annotate( '***', (x_value, y_value), xytext=(0, space), textcoords="offset points", ha='center', va=va, fontsize=20 ) plot_in_subplots()
报错信息
ValueError: Image size of 3719x61904113 pixels is too large. It must be less than 2^16 in each direction
待合并的两张示例图


问题原因
核心原因是代码混用了全局plt接口和指定子图ax接口的操作:当创建多个子图时,全局plt的操作默认作用在最后创建的子图上,会导致其他子图的轴边界、刻度等参数被异常修改,savefig传入bbox_inches="tight"时会自动计算画布所有内容的边界,异常的轴参数会导致计算出超大的画布尺寸,触发报错。
修复方案
- 所有绘图操作全部绑定到传入的子图
ax对象,不要使用全局plt接口操作指定子图的属性,上面代码块中已经标注了所有需要修改的位置 - 可以根据布局需要适当调小刻度、标签的字号,避免自动边界计算时溢出
- 如果不需要自动裁剪画布空白,可以直接去掉
savefig的bbox_inches="tight"参数,也能避免该报错
内容的提问来源于stack exchange,提问作者burcak
相关产品推荐
相关产品推荐

