Skip to content

[Bug]: AttributeError: 'NoneType' object has no attribute 'rowspan' with mpl >=3.7 #25538

Closed

Description

@ocefpaf

Bug summary

With mpl <3.7 this code works the code snippet below works but it is broken in 3.7.0 and 3.7.1. The odd thing is that if I move the colorbar to the last line or if I don't use pandas to the the C plotting it works just fine. I guess there is a bad interaction somewhere that is messing up with the subplots.

Code for reproduction

import matplotlib.pyplot as plt
import pandas as pd

fig = plt.figure()

axes = fig.subplot_mosaic(
    """
    AB
    CC
    """
)

x = [1, 2, 3]
y = [1, 2, 3]

cs0 = axes["A"].scatter(x, y)
cs1 = axes["B"].scatter(x, y)

fig.colorbar(cs0, ax=[axes["A"], axes["B"]], location="right")
axes["C"].plot(x)
pd.DataFrame(x).plot(ax=axes["C"])

Actual outcome

AttributeError                            Traceback (most recent call last)
Cell In[10], line 21
     19 fig.colorbar(cs0, ax=[axes["A"], axes["B"]], location="right")
     20 axes["C"].plot(x)
---> 21 pd.DataFrame(x).plot(ax=axes["C"])
     22 # fig.colorbar(cs0, ax=[axes["A"], axes["B"]], location="right")

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_core.py:1000, in PlotAccessor.__call__(self, *args, **kwargs)
    997             label_name = label_kw or data.columns
    998             data.columns = label_name
-> 1000 return plot_backend.plot(data, kind=kind, **kwargs)

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_matplotlib/__init__.py:71, in plot(data, kind, **kwargs)
     69         kwargs["ax"] = getattr(ax, "left_ax", ax)
     70 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 71 plot_obj.generate()
     72 plot_obj.draw()
     73 return plot_obj.result

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:455, in MPLPlot.generate(self)
    453 self._add_table()
    454 self._make_legend()
--> 455 self._adorn_subplots()
    457 for ax in self.axes:
    458     self._post_plot_logic_common(ax, self.data)

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:679, in MPLPlot._adorn_subplots(self)
    677     all_axes = self._get_subplots()
    678     nrows, ncols = self._get_axes_layout()
--> 679     handle_shared_axes(
    680         axarr=all_axes,
    681         nplots=len(all_axes),
    682         naxes=nrows * ncols,
    683         nrows=nrows,
    684         ncols=ncols,
    685         sharex=self.sharex,
    686         sharey=self.sharey,
    687     )
    689 for ax in self.axes:
    690     ax = getattr(ax, "right_ax", ax)

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_matplotlib/tools.py:410, in handle_shared_axes(axarr, nplots, naxes, nrows, ncols, sharex, sharey)
    408 layout = np.zeros((nrows + 1, ncols + 1), dtype=np.bool_)
    409 for ax in axarr:
--> 410     layout[row_num(ax), col_num(ax)] = ax.get_visible()
    412 for ax in axarr:
    413     # only the last row of subplots should get x labels -> all
    414     # other off layout handles the case that the subplot is
    415     # the last in the column, because below is no subplot/gap.
    416     if not layout[row_num(ax) + 1, col_num(ax)]:

File ~/micromamba/envs/TEST/lib/python3.11/site-packages/pandas/plotting/_matplotlib/tools.py:396, in handle_shared_axes.<locals>.<lambda>(x)
    386 def handle_shared_axes(
    387     axarr: Iterable[Axes],
    388     nplots: int,
   (...)
    393     sharey: bool,
    394 ):
    395     if nplots > 1:
--> 396         row_num = lambda x: x.get_subplotspec().rowspan.start
    397         col_num = lambda x: x.get_subplotspec().colspan.start
    399         if compat.mpl_ge_3_4_0():

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

Expected outcome

Screenshot from 2023-03-23 15-21-12

Additional information

No response

Operating system

No response

Matplotlib Version

3.7.0 and 3.7.1

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

3.11

Jupyter version

6.5.3

Installation

conda

Activity

  1. ksunden commented on Mar 23, 2023

    @ksunden
    Member

    This should likely be reported to Pandas.

    Drilling down, the problem is precisely that the colorbar is not part of the gridspec, and therefore get_subplotspec returns None.

    Since #23573 Subplot is a simple alias of Axes and so all Axes objects have get_subplotspec (but it returns None for axes outside of a gridspec)

    #23573 also contains a direct address of this change in behavior.

    >>> from matplotlib.axes import Subplot
    >>> [ax for ax in fig.get_axes() if isinstance(ax, Subplot)]
    [<Axes: label='A'>, <Axes: label='B'>, <Axes: label='C'>, <Axes: label='<colorbar>'>]
    >>> [x.get_subplotspec() for x in fig.get_axes()]
    [GridSpec(2, 2)[0:1, 0:1], GridSpec(2, 2)[0:1, 1:2], GridSpec(2, 2)[1:2, 0:2], None]
  2. ocefpaf commented on Mar 23, 2023

    @ocefpaf
    ContributorAuthor

    This should likely be reported to Pandas.

    Makes sense. I ended up reporting here b/c it does work with older mpl versions.

  3. tacaswell commented on Mar 24, 2023

    @tacaswell
    Member

    This used to work because

        def _get_subplots(self):
            from matplotlib.axes import Subplot
    
            return [
                ax for ax in self.axes[0].get_figure().get_axes() if isinstance(ax, Subplot)
            ]
    
    

    used to ignore the colorbar axes because it was not a Subplot.

  4. tacaswell commented on Mar 24, 2023

    @tacaswell
    Member

    I could not find a pandas issue, but I opened a PR I think will fix it.

  5. added a commit that references this issue on Nov 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions