0

I'm using gridspec to make a rather complicated figure layout. It works mostly fine without spacing adjustments, but some labels still overlap in the 4 subplots at top right:

import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec as gs

fig = plt.figure(figsize=(30, 22))
TheGrid = gs.GridSpec(9, 12, figure=fig)

# Set up the grid
# Main plot: top left of figure
MainAx = plt.subplot(TheGrid.new_subplotspec((0, 0), colspan=4, rowspan=4)) 
MainAx.set_xlabel("MainAx - x", fontsize=24)
MainAx.set_ylabel("MainAx - y", fontsize=24)

# An empty axis to create some space between MainAx and SubAx1 and SubAx2
VerticalSpacer = plt.subplot(TheGrid.new_subplotspec((0, 4), rowspan=4)) 
VerticalSpacer.axis("off")

# Two small subplots to the right of main plot
SubAx1 = plt.subplot(TheGrid.new_subplotspec((0, 5), colspan=2, rowspan=2))
SubAx2 = plt.subplot(TheGrid.new_subplotspec((2, 5), colspan=2, rowspan=2))
SubAx1.axes.get_xaxis().set_visible(False)

SubAx2.set_xlabel("SubAx2 - x", fontsize=24)
SubAx1.set_ylabel("SubAx1 - y", fontsize=24)
SubAx2.set_ylabel("SubAx2 - y", fontsize=24)

# An empty axis to create some space between SubAx1, SubAx2 and the next axes
VerticalSpacer2 = plt.subplot(TheGrid.new_subplotspec((0, 7), rowspan=4)) 
VerticalSpacer2.axis("off")

# 4 small subplots at top right of figure
Iax1 = plt.subplot(TheGrid.new_subplotspec((0, 8), colspan=2, rowspan=2))
Iax1.set_xlabel("Iax1 - x", fontsize=24)
Iax1.set_ylabel("Iax1 - y", fontsize=24)
Iax1.set_title("Iax1", fontsize=24)

Iax2 = plt.subplot(TheGrid.new_subplotspec((0, 10), colspan=2, rowspan=2))

Iax3 = plt.subplot(TheGrid.new_subplotspec((2, 8), colspan=2, rowspan=2))
Iax3.set_xlabel("Iax3 - x", fontsize=24)
Iax3.set_ylabel("Iax3 - y", fontsize=24)
Iax3.set_title("Iax3", fontsize=24)

Iax4 = plt.subplot(TheGrid.new_subplotspec((2, 10), colspan=2, rowspan=2))

# Empty axis to create some space below the above axes
HorizontalSpacer = plt.subplot(TheGrid.new_subplotspec((4, 0), colspan=10)) 
HorizontalSpacer.axis("off")

# An axis to contain numerous small subplots without labels
SnapshotAx = plt.subplot(TheGrid.new_subplotspec((5, 0), colspan=10, rowspan=4))
SnapshotAxSub = gs.GridSpecFromSubplotSpec(4, 10, subplot_spec=SnapshotAx)

# Plot the snapshots 
row_count = 0
col_count = 0

for i in range(38):
    if (i % 10 == 0):
        col_count = 0 
        if i != 0:
            row_count += 1
    else:
        col_count += 1
        
    temp_ax = plt.subplot(SnapshotAxSub.new_subplotspec((row_count, col_count), colspan=1, rowspan=1))
    temp_ax.plot([1,2,3], [1,2,3])

    # turn off pesky ticks
    temp_ax.axes.get_xaxis().set_visible(False)
    temp_ax.axes.get_yaxis().set_visible(False)

plt.show()

mwe

(Note that the loop over 38 items represents a part of the code in which, at each creation of this figure, I have to create an unknown number of subplots, but not more than about 40. I chose 38 here as an arbitrary number to demonstrate the extent of that axis.)

Strangely, when layout='constrained' is added, the code breaks and throws the following error:

fig = plt.figure(figsize=(30, 22), layout="constrained")
...(all other code the same)...

This throws the following error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File ~/anaconda3/lib/python3.8/site-packages/IPython/core/formatters.py:339, in BaseFormatter.__call__(self, obj)
    337     pass
    338 else:
--> 339     return printer(obj)
    340 # Finally look for special method names
    341 method = get_real_method(obj, self.print_method)

File ~/anaconda3/lib/python3.8/site-packages/IPython/core/pylabtools.py:151, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
    148     from matplotlib.backend_bases import FigureCanvasBase
    149     FigureCanvasBase(fig)
--> 151 fig.canvas.print_figure(bytes_io, **kw)
    152 data = bytes_io.getvalue()
    153 if fmt == 'svg':

File ~/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py:2342, 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 ~/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py:95, 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 ~/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py:72, 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 ~/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py:3134, in Figure.draw(self, renderer)
   3132 if self.axes and self.get_layout_engine() is not None:
   3133     try:
-> 3134         self.get_layout_engine().execute(self)
   3135     except ValueError:
   3136         pass

File ~/anaconda3/lib/python3.8/site-packages/matplotlib/layout_engine.py:253, in ConstrainedLayoutEngine.execute(self, fig)
    250 w_pad = self._params['w_pad'] / width
    251 h_pad = self._params['h_pad'] / height
--> 253 return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad,
    254                              wspace=self._params['wspace'],
    255                              hspace=self._params['hspace'],
    256                              rect=self._params['rect'],
    257                              compress=self._compress)

File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:104, in do_constrained_layout(fig, h_pad, w_pad, hspace, wspace, rect, compress)
    102 renderer = fig._get_renderer()
    103 # make layoutgrid tree...
--> 104 layoutgrids = make_layoutgrids(fig, None, rect=rect)
    105 if not layoutgrids['hasgrids']:
    106     _api.warn_external('There are no gridspecs with layoutgrids. '
    107                        'Possibly did not call parent GridSpec with the'
    108                        ' "figure" keyword')

File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:192, in make_layoutgrids(fig, layoutgrids, rect)
    190     gs = ax.get_gridspec()
    191     if gs is not None:
--> 192         layoutgrids = make_layoutgrids_gs(layoutgrids, gs)
    194 return layoutgrids

File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:233, in make_layoutgrids_gs(layoutgrids, gs)
    227     rep = (gs, 'top')
    228     if rep not in layoutgrids:
    229         layoutgrids[rep] = mlayoutgrid.LayoutGrid(
    230             parent=subspeclb,
    231             name='top',
    232             nrows=1, ncols=1,
--> 233             parent_pos=(subplot_spec.rowspan, subplot_spec.colspan))
    234     layoutgrids[gs] = mlayoutgrid.LayoutGrid(
    235             parent=layoutgrids[rep],
    236             name='gridspec',
    237             nrows=gs._nrows, ncols=gs._ncols,
    238             width_ratios=gs.get_width_ratios(),
    239             height_ratios=gs.get_height_ratios())
    240 return layoutgrids

AttributeError: 'Axes' object has no attribute 'rowspan'

All of the errors highlighted are within Python and Matlotlib. These are installed via Anaconda; version of Python is 3.8.13, and the Matplotlib version is 3.7.1. Matplotlib was previously 3.5, so I tried upgrading it after a few SE/google searches, but this has not helped the problem. I haven't found any resources on this particular error being encountered simply by triggering layout="'constrained'.

Is there something else in my code that could be causing this? Thanks in advance.

dax
  • 45
  • 5

0 Answers0