Jupyter中Matplotlib图表可显示却无法保存的技术求助
Jupyter中Matplotlib图表显示正常但保存空白的问题解决
在Jupyter Notebook中,Matplotlib生成的复合轴图表能够正常显示,但使用多种方法保存时均得到空白图片。
可复现代码
import numpy as np import matplotlib.pyplot as plt # %matplotlib inline from scipy.optimize import fsolve import ast from mpl_toolkits.axisartist.parasite_axes import HostAxes, ParasiteAxes import matplotlib.pyplot as plt basicDict = {'Model_Name': 'TestPump_d258', 'max_pressure': ' 16 бар', 'liquid_pump_temp': ' -15C-110C', 'max_env_temp': ' 40С', 'engine_efficiency_class': ' IE2', 'Net_connection': ' 3~380V/50 Hz', 'rotation_frequency_nominal': ' 2900 1/min', 'Nominal_power_P2': ' P2 15 кВт', 'Nominal_currency': ' 27 А', 'Moisture_protection': ' IP54', 'Isolation_Class': ' F', 'Sucking_pipe_branch': ' DN100', 'Pushing_pipe_branch': ' DN100', 'building_length': ' 480 мм', 'frame': ' GG20', 'working_wheel': ' GG20', 'Shaft': ' 1.4021', 'Weight': ' 335 ', 'flow_rate_Q_m3_h': '[0.00, 11.77, 23.54, 35.31, 47.07, 58.84, 70.61, 82.38, 94.15, 96.04, 105.92, 117.69, 129.45, 141.22, 152.99, 164.76]', 'lift_H_m': '[22.96, 22.92, 22.93, 22.92, 22.80, 22.51, 22.02, 21.28, 20.27, 20.08, 18.98, 17.40, 15.55, 13.45, 11.14, 8.65]', 'Cavitation_NPSH_m': '[1.14, 1.17, 1.20, 1.24, 1.29, 1.37, 1.46, 1.59, 1.76, 1.79, 1.98, 2.26, 2.61, 3.05, 3.58, 4.23]', 'motor_power_P2_kW': '[3.57, 3.84, 4.21, 4.67, 5.17, 5.68, 6.20, 6.70, 7.17, 7.24, 7.60, 7.99, 8.35, 8.67, 8.98, 9.28]', 'efficiency': '[0.00, 4.00, 8.00, 12.00, 16.00, 20.00, 24.00, 28.00, 32.00, 36.00, 40.00, 44.00, 41.00, 38.00, 35.00, 32.00]', 'Scale_Table': '{"L":500,"B":430,"H":855,"C1":160,"B1":220,"A":160,"C2":120,"B2":180,"4-d 1":"4-18"}', 'pump_wiring_connection_figure': 'connection_figures/test_connection.png', 'pump_figure': 'pump_figures/Test_pump_image.png'} Q = 100 H = 18 flow_rate_Q_m3_h = np.array(ast.literal_eval(basicDict["flow_rate_Q_m3_h"])) lift_H_m = np.array(ast.literal_eval(basicDict["lift_H_m"])) Cavitation_NPSH_m = np.array(ast.literal_eval(basicDict["Cavitation_NPSH_m"])) motor_power_P2_kW = np.array(ast.literal_eval(basicDict["motor_power_P2_kW"])) efficiency = np.array(ast.literal_eval(basicDict["efficiency"])) def Formula(Q, H, EfficiancyValue): Hvx =(((Q/100*EfficiancyValue)/Q)**2)*H return Hvx def calculate_Hvx_Line(Q, H, EfficiancyMax): calculatedPoints = [] percents = range(0,EfficiancyMax) for each in percents: calculatedPoints.append(Formula(Q, H, each)) return calculatedPoints # Строим модели полиномиальной регрессии для данных model5 = np.poly1d (np.polyfit (flow_rate_Q_m3_h, lift_H_m , 6)) # на графике будет сглаженая, построеная по модели версия pumpcurve1 = [] for each in range(int(flow_rate_Q_m3_h.max())): pumpcurve1.append(model5(each)) # график по расчету calculatedPoints = np.array(calculate_Hvx_Line(Q, H, 120)) # Ищем x точки соприкосновения двух моделей def F(k): return Formula(Q, H, k)-model5(k) Qnew = fsolve(F, 42)[0] # y точки соприкосновения двух моделей h = Formula(Q, H, Qnew) # плотим графики и точку fig = plt.figure() host = fig.add_axes([1.2, 1.2, 1.2, 1.2], axes_class=HostAxes) par1 = ParasiteAxes(host, sharex=host) par2 = ParasiteAxes(host, sharex=host) host.parasites.append(par1) host.parasites.append(par2) host.axis["right"].set_visible(False) par1.axis["right"].set_visible(True) par1.axis["right"].major_ticklabels.set_visible(True) par1.axis["right"].label.set_visible(True) # плотим графики и точку p1, = host.plot([Qnew], [h], 'o', markersize=8, color = "r") p1, = host.plot([Qnew], [h], 'o', markersize=3, color = "k") p1, = host.plot(np.linspace(0, 120,120),calculatedPoints, label="calculated") p1, = host.plot(np.linspace(0, len(pumpcurve1),len(pumpcurve1)),pumpcurve1, label="flow_rate_Q_m3_h") max_x = (int(np.array(flow_rate_Q_m3_h).max())//10)*(10)+10 host.set_xlim([0,max_x]) host.plot([0, Qnew],[h, h], "k--") host.text(2, h-1, fr"h (m) = {h:.2f}", color="k",fontsize=10) max_y = (int(np.array(calculatedPoints).max())//5)*(5+1) host.set_ylim([0,max_y]) host.plot([Qnew, Qnew],[0, h], "k--") host.text(Qnew + 2, 0.5, fr"Q (m$^3$/s) = {Qnew:.2f}", color="k") p2, = par2.plot(np.linspace(0, np.max(flow_rate_Q_m3_h),len(flow_rate_Q_m3_h)),efficiency, label="efficiency") ef_max = (int(np.array(efficiency).max())//5)*(5+1) par1.set_ylim(0, ef_max) host.set_xlabel(r'Q (m$^3$/s)') host.set_ylabel('h (m)') par1.set_ylabel("efficiency (%)") host.legend() host.grid(True) host.axis["left"].label.set_color(p1.get_color()) par1.axis["right"].label.set_color(p2.get_color()) plt.show()
尝试过的保存方法
- 方法1:
import cv2 as cv from PIL import Image, ImageFilter fig1 = plt.gcf() plt.draw() fig1.savefig('im.png') width, height = fig.get_size_inches() * fig.get_dpi() width , height = int(width), int(height)
- 方法2:
canvas = FigureCanvas(fig) canvas.draw() buf = canvas.buffer_rgba() X = np.asarray(buf) cv.imshow("ready",X.reshape(height, width,4))
- 方法3:
canvas = FigureCanvas(fig) image = np.frombuffer(canvas.tostring_rgb(), dtype='uint8') cv.imshow("ready",image.reshape(height, width, 3))
- 方法4:
def fig2img(fig): """Convert a Matplotlib figure to a PIL Image and return it""" import io buf = io.BytesIO() fig.savefig(buf) buf.seek(0) img = Image.open(buf) return img im = np.uint8(fig2img(fig)) cv.imshow("ready",im) cv.waitKey(00)
问题原因
- Axes位置超出画布可视区域:创建
HostAxes时使用的参数[1.2, 1.2, 1.2, 1.2]是[left, bottom, width, height]的归一化坐标(范围0-1),该参数把图表绘制在了画布默认区域之外,Jupyter显示时会自动调整画布范围,但保存时只会保存默认画布区域,导致空白。 - plt.show()释放资源:
plt.show()调用后,Matplotlib会释放figure的绘图资源,后续再保存figure会丢失内容。
有效解决方法
方法1:调整Axes位置并在plt.show()前保存
修改代码中创建HostAxes的部分,使用合理的归一化位置参数,并在plt.show()之前调用savefig:
# ... 保留原有代码 ... # 调整host axes的位置参数到画布可视范围内 host = fig.add_axes([0.1, 0.1, 0.8, 0.8], axes_class=HostAxes) # ... 保留原有绘图代码 ... # 在plt.show()之前保存图片,bbox_inches='tight'确保所有元素被保存 fig.savefig('pump_chart.png', dpi=100, bbox_inches='tight') plt.show()
方法2:重新绘制Figure后保存
如果必须在plt.show()之后保存,可以重新执行绘图逻辑(不调用plt.show())再保存:
# 重新构建绘图逻辑,不调用plt.show() fig = plt.figure() host = fig.add_axes([0.1, 0.1, 0.8, 0.8], axes_class=HostAxes) # ... 完整的绘图代码 ... # 直接保存图片 fig.savefig('pump_chart.png', dpi=100, bbox_inches='tight')
方法3:使用Jupyter内置保存功能
在Jupyter中,图表显示后直接点击图表下方的保存按钮,选择PNG/SVG等格式,该方法会自动处理画布范围问题,适合快速保存。
内容的提问来源于stack exchange,提问作者Vitaly Skrinkovich
相关产品推荐
相关产品推荐

