Skip to content

ENH: reuse oldgridspec is possible... #17347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 13, 2020

Conversation

jklymak
Copy link
Member

@jklymak jklymak commented May 6, 2020

PR Summary

A major constraint of constrained_layout is that it never worked with add_subplot(2,3,1) because a new gridspec was created each time. This PR causes the gridspec to be reused if nrows and ncols are the same.

Replaces #11441.

I decided that the grid specs should be exactly the same, so add_subplot(4, 4, 2) would be a different grid than add_subplot(2, 2, 4) despite the fact they could theoretically be made the same. Basically if you want those two axes to be in the same layout, you should specify them as
add_subplot(4, 4, 2) and add_subplot(4, 4, (11, 16))

Update: Now works with subplotgrid as well.

Closes #15821

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@jklymak jklymak added this to the v3.4.0 milestone May 7, 2020
@jklymak jklymak added the topic: geometry manager LayoutEngine, Constrained layout, Tight layout label May 7, 2020
@jklymak jklymak force-pushed the enh-reuse-oldgrdspec branch from 3289ff7 to 3d9fbcc Compare May 7, 2020 02:52
@jklymak jklymak force-pushed the enh-reuse-oldgrdspec branch from ca184e9 to 93142c6 Compare May 25, 2020 17:51
@jklymak jklymak marked this pull request as ready for review May 25, 2020 19:18
@jklymak jklymak force-pushed the enh-reuse-oldgrdspec branch from c4225f3 to b0b9792 Compare May 25, 2020 19:19
@jklymak jklymak requested a review from anntzer May 26, 2020 04:49
@jklymak
Copy link
Member Author

jklymak commented May 26, 2020

@anntzer you looked at a previous version of this, so if you had time ...

ax3 = plt.subplot(122)
ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 3)
ax3 = plt.subplot(2, 2, (2, 4))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps add a comment explaining what this does?

# This is probably OK becase this whole logic tree
# is for when the user is doing simple things with the
# add_subplot command. Complicated stuff, the proper
# gridspec is passed in...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps further add a comment that this is only for cases like subplot(ijk) where the user cannot be explicitly passing in e.g. width_ratios or height_ratios, so we don't need to try to match these.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified to

# For complicated layouts like subgridspecs the proper gridspec is passed in...

I think width_ratios etc are not the problem, but nested gridspecs are not particularly handled here. But I don't know how you add a subplot to a nested gridspec without grabbing its subplotspec, so I think this is fine.

Copy link
Contributor

@anntzer anntzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some clarifications suggested, but lgtm. Dunno if it's worth a whatsnew.

@jklymak
Copy link
Member Author

jklymak commented May 26, 2020

...ooops, yes it needs a Whats new.

@jklymak jklymak force-pushed the enh-reuse-oldgrdspec branch from b0b9792 to e318138 Compare May 26, 2020 14:41
ggs = ggs.get_topmost_subplotspec().get_gridspec()
(nrow, ncol) = ggs.get_geometry()
if nrow == nrows and ncol == ncols:
gs = ggs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps you can just return ggs here? (dunno if it matters whether you pick the first or the last match, hopefully there's only one anyways...) and then you don't need to have gs = None at the top, yada yada.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@tacaswell
Copy link
Member

I am 👍 in principle, have not read the code carefully. I support this being merged on 1 very careful reviewer.

Copy link
Member

@efiring efiring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarifications and a couple condensations...

as the top level gridspec already in the figure. i.e. ``plt.subplot(2, 1, 2)``
will use the same gridspec as ``plt.subplot(2, 1, 1)`` and the
``constrained_layout=True`` option to `~.figure.Figure` will work. Note that
mixing ``nrows`` and ``ncols`` will *not* work: ``plt.subplot(2, 2, 1)``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression "Note that mixing nrows and ncols will not work:..." is confusing; it would be clearer here if you just said, "In contrast, ..."

mixing ``nrows`` and ``ncols`` will *not* work: ``plt.subplot(2, 2, 1)``
followed by ``plt.subplots(2, 1, 2)`` will produce two gridspecs, and
``constrained_layout=True`` will give bad results. In order to get the
same effect, the second call can specify the cells the second axes is meant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"same" -> "desired"

# like subgridspecs the proper gridspec is passed in...
gs = gs.get_topmost_subplotspec().get_gridspec()
(nrow, ncol) = gs.get_geometry()
if nrow == nrows and ncol == ncols:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: 2 lines could be condensed to

if gs.get_geometry() == (nrows, ncols):

@@ -667,7 +690,7 @@ def _from_subplot_args(figure, args):
f"Single argument to subplot must be a three-digit "
f"integer, not {arg}") from None
# num - 1 for converting from MATLAB to python indexing
return GridSpec(rows, cols, figure=figure)[num - 1]
i = j = num
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the comment above this line no longer belongs there.

else:
if not isinstance(num, Integral):
cbook.warn_deprecated("3.3", message=message)
num = int(num)
if num < 1 or num > rows*cols:
raise ValueError(
f"num must be 1 <= num <= {rows*cols}, not {num}")
return gs[num - 1] # -1 due to MATLAB indexing.
else:
i = j = num
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can delete the "else" and unindent the line.

``constrained_layout=True`` will give bad results. In order to get the
same effect, the second call can specify the cells the second axes is meant
to cover: ``plt.subplots(2, 2, (2, 4))``, or the more pythonic
``plt.subplots2grid((2, 2), (0, 1), rowspan=2)`` can be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"subplots2grid" -> "subplot2grid"


Previously ``constrained_layout`` depended on a single ``GridSpec``
for each logical layout on a figure, whereas ``plt.subplot`` added
a new ``GridSpec`` for each call. Now ``plt.subplot`` attempts to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constrained layout still depends on a single Gridspec, doesn't it? Maybe change to "Previously, plt.subplot added a new GridSpec with each call, but constrained layout depends on working with a single GridSpec for...figure. Now, plt.subplot..."

@@ -0,0 +1,17 @@
Constrained layout now supports subplot and subplot2grid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, "Subplot and subplot2grid can now work with constrained layout". (I used "can" because they work only if called appropriately.)

@jklymak jklymak force-pushed the enh-reuse-oldgrdspec branch from 126f6a8 to 487ac80 Compare July 13, 2020 21:47
@jklymak
Copy link
Member Author

jklymak commented Jul 13, 2020

Thanks @efiring. Made the small changes and largely rewrote the what's new based on your suggestions.

@efiring efiring merged commit db3ecd2 into matplotlib:master Jul 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should constrained_layout work as plt.figure() argument?
5 participants