Description
Bug summary
When creating a stacked bar chart and annotating its components by use of the bar_label
method, a RuntimeError("Unknown return type")
exception is raised the moment one of the bar labels comes to rest outside the specified Y limits.
Code for reproduction
#!/usr/bin/env python3
from matplotlib import pyplot
import numpy as np
def main():
categories = ["Foo", "Bar", "Baz", "Barbaz"]
data = {
"Apples": [0, 1, 2, 3],
"Oranges": [2, 5, 27, 1],
"Pears": [17, 22, 15, 10],
}
grid = (1, 1)
fig = pyplot.figure(figsize=(9, 9), dpi=300, layout="constrained")
ax = pyplot.subplot2grid(grid, (0, 0), fig=fig)
bottom = np.zeros(len(categories))
width = 0.5
for fruit, values in data.items():
p = ax.bar(
categories,
values,
width,
label=fruit,
bottom=bottom,
)
ax.bar_label(p, label_type='center')
bottom += values
# The exception happens if the Y-limit of the plot is chosen too low.
# This limit is fine, the whole graph fits
# ax.set(ylim=(0, 50))
# This limit is fine as well, even though it cuts off the top of the
# topmost bar.
# ax.set(ylim=(0, 35))
# This limit is fine still, with the topmost label ('15' of the 'Baz'
# category) being on the top border of the plot.
# ax.set(ylim=(0, 29))
# This limit produces an exception, likely as the topmost label ('15' of
# the 'Baz' category) cannot be placed anymore, since its bar's lower
# end starts at 2+27 = 29.
ax.set(ylim=(0, 28))
fig.savefig("/tmp/matplotlib_example.png", format="png")
if __name__ == '__main__':
main()
Actual outcome
(matplotlib-bar-label-mre)matplotlib-bar-label-mre › python demo.py
Traceback (most recent call last):
File "/home/michael/dev/python/matplotlib-bar-label-mre/demo.py", line 56, in <module>
main()
File "/home/michael/dev/python/matplotlib-bar-label-mre/demo.py", line 53, in main
fig.savefig("/tmp/matplotlib_example.png", format="png")
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/figure.py", line 3343, in savefig
self.canvas.print_figure(fname, **kwargs)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/backend_bases.py", line 2342, in print_figure
self.figure.draw(renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/artist.py", line 95, in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/figure.py", line 3134, in draw
self.get_layout_engine().execute(self)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/layout_engine.py", line 253, in execute
return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad,
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/_constrained_layout.py", line 119, in do_constrained_layout
make_layout_margins(layoutgrids, fig, renderer, h_pad=h_pad,
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/_constrained_layout.py", line 369, in make_layout_margins
pos, bbox = get_pos_and_bbox(ax, renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/_constrained_layout.py", line 617, in get_pos_and_bbox
tightbbox = martist._get_tightbbox_for_layout_only(ax, renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/artist.py", line 1415, in _get_tightbbox_for_layout_only
return obj.get_tightbbox(*args, **{**kwargs, "for_layout_only": True})
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/axes/_base.py", line 4408, in get_tightbbox
bbox = a.get_tightbbox(renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 2064, in get_tightbbox
return super().get_tightbbox(renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/artist.py", line 367, in get_tightbbox
bbox = self.get_window_extent(renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 2050, in get_window_extent
self.update_positions(self._renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 1954, in update_positions
self.set_transform(self._get_xy_transform(renderer, self.anncoords))
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 1538, in _get_xy_transform
xy0 = self._get_ref_xy(renderer)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 1569, in _get_ref_xy
return self._get_xy(renderer, *self.xy, self.xycoords)
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 1478, in _get_xy
return self._get_xy_transform(renderer, s).transform((x, y))
File "/home/michael/.local/share/virtualenvs/matplotlib-bar-label-mre/lib/python3.10/site-packages/matplotlib/text.py", line 1496, in _get_xy_transform
raise RuntimeError("Unknown return type")
RuntimeError: Unknown return type
Expected outcome
I'd expect the corresponding section of the bar, along with its label, to simply be outside the visible plot area. Or at least a more descriptive error message.
Additional information
The exception seems to happen the moment one of the bar labels cannot fit in the visible plot area anymore. This can be seen by starting with a large Y limit, and slowly lowering it.
As the Y limit approaches the position of the topmost label, it will be shifted down on its own. However the moment the Y limit is set low enough that the topmost bar's component is fully outside the visible area, the exception arises.
The (mostly) minimal reproducible example above contains several Y limits to indicate this happening.
Operating system
Arch Linux, Linux 6.2.5
Matplotlib Version
3.7.1
Matplotlib Backend
TkAgg
Python version
3.10.9
Jupyter version
Not applicable
Installation
pip