使用Matplotlib SubFigure添加等高线标签时遇_cachedRenderer属性错误
Matplotlib SubFigure 中使用 ax.clabel 报错 AttributeError 的解决办法
在使用Matplotlib的SubFigure布局时,调用ax.clabel()为等高线添加标签会抛出AttributeError: 'SubFigure' object has no attribute '_cachedRenderer'错误,以下是问题复现、原因分析及解决办法:
复现代码
import matplotlib.pyplot as plt import numpy as np delta = 0.025 x = np.arange(-3.0, 3.0, delta) y = np.arange(-2.0, 2.0, delta) X, Y = np.meshgrid(x, y) Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) Z = (Z1 - Z2) * 2 fig = plt.figure(constrained_layout=True) subfigs = fig.subfigures(1, 2) for i, subfig in enumerate(subfigs.flat): axs = subfig.subplots(2, 1) for j, ax in enumerate(axs): CS = ax.contour(X, Y, Z) ax.clabel(CS) plt.show()
错误信息
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_35648\2613963086.py in <module> 17 for j, ax in enumerate(axs): 18 CS = ax.contour(X, Y, Z) ---> 19 ax.clabel(CS) 20 plt.show() ~\anaconda3\lib\site-packages\matplotlib\axes\_axes.py in clabel(self, CS, levels, **kwargs) 6333 All other parameters are documented in `~.ContourLabeler.clabel`. 6334 """ -> 6335 return CS.clabel(levels, **kwargs) 6336 6337 #### Data analysis ~\anaconda3\lib\site-packages\matplotlib\contour.py in clabel(self, levels, fontsize, inline, inline_spacing, fmt, colors, use_clabeltext, manual, rightside_up, zorder) 233 self, inline, inline_spacing)) 234 else: -> 235 self.labels(inline, inline_spacing) 236 237 self.labelTextsList = cbook.silent_list('text.Text', self.labelTexts) ~\anaconda3\lib\site-packages\matplotlib\contour.py in labels(self, inline, inline_spacing) 580 con = self.collections[icon] 581 trans = con.get_transform() -> 582 lw = self._get_nth_label_width(idx) 583 additions = [] 584 paths = con.get_paths() ~\anaconda3\lib\site-packages\matplotlib\contour.py in _get_nth_label_width(self, nth) 283 size=self.labelFontSizeList[nth], 284 fontproperties=self.labelFontProps) -> 285 .get_window_extent(mpl.tight_layout.get_renderer(fig)).width) 286 287 @_api.deprecated("3.5") ~\anaconda3\lib\site-packages\matplotlib\tight_layout.py in get_renderer(fig) 204 205 def get_renderer(fig): -> 206 if fig._cachedRenderer: 207 return fig._cachedRenderer 208 else: AttributeError: 'SubFigure' object has no attribute '_cachedRenderer'
问题原因
报错根源在于Matplotlib的等高线标签计算逻辑中,错误地将SubFigure对象传递给了get_renderer()函数,而SubFigure并没有主Figure才有的_cachedRenderer属性,导致属性查找失败。
可行解决方案
方案1:禁用inline标签模式
调用ax.clabel()时添加inline=False参数,绕开需要计算标签宽度的逻辑:
import matplotlib.pyplot as plt import numpy as np delta = 0.025 x = np.arange(-3.0, 3.0, delta) y = np.arange(-2.0, 2.0, delta) X, Y = np.meshgrid(x, y) Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) Z = (Z1 - Z2) * 2 fig = plt.figure(constrained_layout=True) subfigs = fig.subfigures(1, 2) for i, subfig in enumerate(subfigs.flat): axs = subfig.subplots(2, 1) for j, ax in enumerate(axs): CS = ax.contour(X, Y, Z) # 添加inline=False参数 ax.clabel(CS, inline=False) plt.show()
此方法会将标签显示在等高线外侧,而非嵌入线条中,但能避免报错。
方案2:提前触发主Figure渲染
在创建SubFigure之前,先调用fig.canvas.draw()生成主Figure的缓存渲染器,这样后续clabel逻辑即使误传SubFigure,也能 fallback 使用主渲染器:
import matplotlib.pyplot as plt import numpy as np delta = 0.025 x = np.arange(-3.0, 3.0, delta) y = np.arange(-2.0, 2.0, delta) X, Y = np.meshgrid(x, y) Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) Z = (Z1 - Z2) * 2 fig = plt.figure(constrained_layout=True) # 提前触发主图渲染,生成cachedRenderer fig.canvas.draw() subfigs = fig.subfigures(1, 2) for i, subfig in enumerate(subfigs.flat): axs = subfig.subplots(2, 1) for j, ax in enumerate(axs): CS = ax.contour(X, Y, Z) ax.clabel(CS) plt.show()
此方法保留inline标签的显示效果,是更贴近需求的临时解决办法。
方案3:替换为普通Subplots布局
如果SubFigure并非必须,可以改用传统的fig.subplots()布局,避免SubFigure带来的兼容性问题:
import matplotlib.pyplot as plt import numpy as np delta = 0.025 x = np.arange(-3.0, 3.0, delta) y = np.arange(-2.0, 2.0, delta) X, Y = np.meshgrid(x, y) Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) Z = (Z1 - Z2) * 2 fig, axs = plt.subplots(2, 2, constrained_layout=True) for ax in axs.flat: CS = ax.contour(X, Y, Z) ax.clabel(CS) plt.show()
内容的提问来源于stack exchange,提问作者tobih125
相关产品推荐
相关产品推荐

