Matplotlib极坐标图添加多元素时触发StopIteration错误的原因排查
Matplotlib极坐标图添加多元素时触发StopIteration错误的原因排查
我最近在尝试用Matplotlib绘制极坐标图,还想添加一个注释箭头指向图内的某个点,但只要同时加入多个绘图元素,就会触发StopIteration错误。
我暂时没法定位到更精确的触发条件,不过发现只要满足以下任意一种情况,图就能正常显示:
- 给
r换一组不同的随机数值 - 注释掉
ax.set_rticks(...)这一行(自定义径向刻度的代码) - 注释掉
ax.annotate(...)(创建箭头的代码) - 注释掉
ax.scatter(an_theta, an_r)(标记箭头终点的代码)
从报错栈的信息来看,错误应该是在绘制注释箭头的环节触发的。我当前的运行环境是JupyterLab 4.2.5(使用内嵌绘图模式)、Matplotlib 3.7.1。
可复现代码
import numpy as np import matplotlib.pyplot as plt # Radius, random numbers in [0, 1) rng = np.random.default_rng(seed=42) r = rng.random((24,)) # 0 to 2pi theta = np.linspace(0, 2, 24, endpoint=False) * np.pi fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) # Data to plot ax.plot(theta, r) ax.scatter(theta, r, c=r, cmap='viridis') ax.set_rticks([1, 2, 3]) # Less radial ticks ax.grid(True) # Where the annotation arrow should point an_r = 0.670 an_theta = 0.820 # Visualise arrow end-point ax.scatter(an_theta, an_r) # Create arrow ax.annotate('', xy=(an_theta, an_r), # theta, radius xytext=(0, 0), arrowprops=dict(facecolor='black', shrink=0), horizontalalignment='right' ) plt.show()
报错信息
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/IPython/core/formatters.py:340](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/IPython/core/formatters.py#line=339), in BaseFormatter.__call__(self, obj) 338 pass 339 else: --> 340 return printer(obj) 341 # Finally look for special method names 342 method = get_real_method(obj, self.print_method) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/IPython/core/pylabtools.py:152](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/IPython/core/pylabtools.py#line=151), in print_figure(fig, fmt, bbox_inches, base64, **kwargs) 149 from matplotlib.backend_bases import FigureCanvasBase 150 FigureCanvasBase(fig) --> 152 fig.canvas.print_figure(bytes_io, **kw) 153 data = bytes_io.getvalue() 154 if fmt == 'svg': File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/backend_bases.py:2342](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/backend_bases.py#line=2341), in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2336 renderer = _get_renderer( 2337 self.figure, 2338 functools.partial( 2339 print_method, orientation=orientation) 2340 ) 2341 with getattr(renderer, "_draw_disabled", nullcontext)(): -> 2342 self.figure.draw(renderer) 2344 if bbox_inches: 2345 if bbox_inches == "tight": File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:95](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=94), in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs) 93 @wraps(draw) 94 def draw_wrapper(artist, renderer, *args, **kwargs): ---> 95 result = draw(artist, renderer, *args, **kwargs) 96 if renderer._rasterizing: 97 renderer.stop_rasterizing() File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:72](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=71), in allow_rasterization.<locals>.draw_wrapper(artist, renderer) 69 if artist.get_agg_filter() is not None: 70 renderer.start_filter() ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/figure.py:3140](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/figure.py#line=3139), in Figure.draw(self, renderer) 3137 # ValueError can occur when resizing a window. 3139 self.patch.draw(renderer) -> 3140 mimage._draw_list_compositing_images( 3141 renderer, self, artists, self.suppressComposite) 3143 for sfig in self.subfigs: 3144 sfig.draw(renderer) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/image.py:131](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/image.py#line=130), in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 129 if not_composite or not has_images: 130 for a in artists: --> 131 a.draw(renderer) 132 else: 133 # Composite any adjacent images together 134 image_group = [] File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:39](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=38), in _prevent_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs) 36 renderer.stop_rasterizing() 37 renderer._rasterizing = False ---> 39 return draw(artist, renderer, *args, **kwargs) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/projections/polar.py:1037](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/projections/polar.py#line=1036), in PolarAxes.draw(self, renderer) 1034 self.yaxis.reset_ticks() 1035 self.yaxis.set_clip_path(self.patch) -> 1037 super().draw(renderer) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:72](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=71), in allow_rasterization.<locals>.draw_wrapper(artist, renderer) 69 if artist.get_agg_filter() is not None: 70 renderer.start_filter() ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/axes/_base.py:3064](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/axes/_base.py#line=3063), in _AxesBase.draw(self, renderer) 3061 if artists_rasterized: 3062 _draw_rasterized(self.figure, artists_rasterized, renderer) -> 3064 mimage._draw_list_compositing_images( 3065 renderer, self, artists, self.figure.suppressComposite) 3067 renderer.close_group('axes') 3068 self.stale = False File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/image.py:131](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/image.py#line=130), in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 129 if not_composite or not has_images: 130 for a in artists: --> 131 a.draw(renderer) 132 else: 133 # Composite any adjacent images together 134 image_group = [] File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:72](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=71), in allow_rasterization.<locals>.draw_wrapper(artist, renderer) 69 if artist.get_agg_filter() is not None: 70 renderer.start_filter() ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/text.py:2032](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/text.py#line=2031), in Annotation.draw(self, renderer) 2030 if self.arrow_patch.figure is None and self.figure is not None: 2031 self.arrow_patch.figure = self.figure -> 2032 self.arrow_patch.draw(renderer) 2033 # Draw text, including FancyBboxPatch, after FancyArrowPatch. 2034 # Otherwise, a wedge arrowstyle can land partly on top of the Bbox. 2035 Text.draw(self, renderer) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py:39](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/artist.py#line=38), in _prevent_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs) 36 renderer.stop_rasterizing() 37 renderer._rasterizing = False ---> 39 return draw(artist, renderer, *args, **kwargs) File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/patches.py:4352](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/patches.py#line=4351), in FancyArrowPatch.draw(self, renderer) 4348 # FIXME: dpi_cor is for the dpi-dependency of the linewidth. There 4349 # could be room for improvement. Maybe _get_path_in_displaycoord could 4350 # take a renderer argument, but get_path should be adapted too. 4351 self._dpi_cor = renderer.points_to_pixels(1.) -> 4352 path, fillable = self._get_path_in_displaycoord() 4354 if not np.iterable(fillable): 4355 path = [path] File [~/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/patches.py:4327](/home/preclineu/.conda/envs/graph_svd/lib/python3.11/site-packages/matplotlib/patches.py#line=4326), in FancyArrowPatch._get_path_in_displaycoord(self) 4325 posB = self._convert_xy_units(self._posA_posB[1]) 4326 (posA,
问题分析
从报错栈的调用路径可以看到,错误最终出现在FancyArrowPatch._get_path_in_displaycoord方法中,也就是绘制注释箭头的环节。结合那些能规避错误的操作来看,这应该是Matplotlib在处理极坐标下自定义径向刻度+注释箭头的组合场景时,内部迭代逻辑出现了异常,属于Matplotlib的已知bug。
备注:内容来源于stack exchange,提问作者Valkje
相关产品推荐
相关产品推荐

