Skip to content

Unexpected change in behavior in plt.subplot #19432

Closed

Description

@astrofrog

Bug report

Bug summary

#19153 introduced some changes that had (I think) unintended consequences when passing custom kwargs to projections

Code for reproduction

Prior to #19153, the following code returned False, that is, two different axes were created:

from astropy.wcs import WCS
import matplotlib.pyplot as plt

wcs1 = WCS(naxis=3)
wcs1.wcs.ctype = ['x', 'y', 'z']

ax1 = plt.subplot(1, 1, 1, projection=wcs1, slices=('x', 'y', 1))

wcs2 = WCS(naxis=3)
wcs2.wcs.ctype = ['a', 'b', 'c']

ax2 = plt.subplot(1, 1, 1, projection=wcs2, slices=('x', 2, 'y'))

print(ax1 is ax2)

No deprecation warning or future warning was raised in this case.

Following #19153, this silently ignores the projection and slices in the second call and the print statement returns True.

I don't think #19153 was intended to start making plt.subplot return the same axes in some cases, rather I thought the goal of that PR was to remove re-use of axes in certain circumstances, so I think this is a regression/bug

Actual outcome

ax1 and ax2 are the same

Expected outcome

ax1 and ax2 should be different

Activity

  1. added
    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
    on Feb 2, 2021
  2. added this to the v3.4.0 milestone on Feb 2, 2021
  3. lpsinger commented on Feb 2, 2021

    @lpsinger
    Contributor

    This is the expected behavior. From https://matplotlib.org/devdocs/users/next_whats_new/axes_kwargs_collision.html:

    The behavior of the functions to create new axes (pyplot.axes, pyplot.subplot, figure.Figure.add_axes, figure.Figure.add_subplot) has changed. In the past, these functions would detect if you were attempting to create Axes with the same keyword arguments as already-existing axes in the current figure, and if so, they would return the existing Axes. Now, these functions will always create new Axes. A special exception is pyplot.subplot, which will reuse any existing subplot with a matching subplot spec. However, if there is a subplot with a matching subplot spec, then that subplot will be returned, even if the keyword arguments with which it was created differ.

  4. dstansby commented on Feb 2, 2021

    @dstansby
    Member

    It seems like even if this is new expected behaviour, we shouldn't be making behaviour changes without a suitable deprecation period, and it sounds like there wasn't any deprecation for the example above.

    So, for 3.4 I think we should

    • Re-instate the previous behaviour for the case above (same subplot spec, different projections)
    • Add a deprecation warning saying that the behaviour will change in the future
  5. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    @lpsinger that is a pretty radical change that might break things for a lot of astropy users, and I don't think something like this can be done without a deprecation warning of some kind (the example I showed above did not show any deprecation warning). This seems like a step backwards - what is the motivation for breaking this behavior? I'm completely on board with no longer automatically returning the same axes in many cases but why change things for the example above from not returning the same to returning the same?

  6. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    Just out of curiosity, what is the motivation for breaking cases like the example I showed? Why would a user ever want the same axes object if the projection is different?

  7. lpsinger commented on Feb 2, 2021

    @lpsinger
    Contributor

    Just out of curiosity, what is the motivation for breaking cases like the example I showed? Why would a user ever want the same axes object if the projection is different?

    For the motivation, see #18832.

  8. dstansby commented on Feb 2, 2021

    @dstansby
    Member

    Indeed, our docs even said that if you had unique args and kwargs you would get a new subplot, so this is definitely a bug that needs fixing before 3.4 is released:

    If the figure already has a subplot with key (*args*,
    *kwargs*) then it will simply make that subplot current and
    return it.  This behavior is deprecated. Meanwhile, if you do
    not want this behavior (i.e., you want to force the creation of a
    new subplot), you must use a unique set of args and kwargs.
    
  9. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    @lpsinger - but this just refers to the case where kwargs are the same:

    In Matplotlib 2.1, the behavior of reusing existing axes when created with the same arguments was deprecated (see #9037). The request is to finally remove this behavior.

    I don't see any good reason why in my example above ax1 and ax2 should be the same since the kwargs are clearly different?

    Again, this is a major change that is going to affect a lot of users - think about users who loop over files and in the loop call plt.subplot with different projections each time - now they will silently get the same axes over and over with the projection from the first file.

  10. lpsinger commented on Feb 2, 2021

    @lpsinger
    Contributor

    Indeed, our docs even said that if you had unique args and kwargs you would get a new subplot, so this is definitely a bug that needs fixing before 3.4 is released:

    If the figure already has a subplot with key (*args*,
    *kwargs*) then it will simply make that subplot current and
    return it.  This behavior is deprecated. Meanwhile, if you do
    not want this behavior (i.e., you want to force the creation of a
    new subplot), you must use a unique set of args and kwargs.
    

    It was an oversight that this paragraph should have been removed in #19153. See #19433.

  11. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    @lpsinger - could you clarify though, why would a user ever want the same axes if the projection and kwargs are different?

  12. lpsinger commented on Feb 2, 2021

    @lpsinger
    Contributor

    @lpsinger - could you clarify though, why would a user ever want the same axes if the projection and kwargs are different?

    As I wrote in #18832:

    My immediate motivation is that I have a collection of projection classes that take as a keyword argument an instance of astropy.coordinates.SkyCoord, which as of Astropy 4.1 are no longer hashable (see also astropy/astropy#9857). This unexpectedly broke my code because I had not realized that projection keyword arguments had to be hashable.

    While researching why Matplotlib required projection keyword arguments to be hashable, I discovered that it was in order to support axes reuse. That was my rationale for removing axes reuse, except (as was suggested by @tacaswell in #18978 (comment)) the one special case of plt.subplot when reusing existing subplots with the same subplot spec.

  13. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    @lpsinger - I can see why removing axes reuse makes sense, but I don't see the motivation for introducing new axes reuse behavior where it did not previously exist?

  14. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    My reading of #18978 was that @tacaswell did not want to break certain behaviors (e.g. calling plt.subplot(222) twice should return the same object) but I don't think it was his intent to mean that examples such as the one here should now be changed to return the same axes? (@tacaswell can you comment on this?)

  15. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    To be clear, if people want to keep this new behavior, I think that:

  16. 11 remaining items

  17. tacaswell commented on Feb 2, 2021

    @tacaswell
    Member

    That's right, that behavior is (supposed to be) preserved. Is it?

    It is, but I was going down the path of sorting out how to resolve "we have N axes at this location, we need to see if any of them match .." so the diff is even smaller than I anticipated.

  18. tacaswell commented on Feb 2, 2021

    @tacaswell
    Member

    See #19438 which I think gets everything we want:

    • no hashing of kwargs
    • multiple calls with matching (and no) kwargs return the same Axes object
    • multiple call with different kwargs create new axes (and continue to implicitly delete fully over-lapped axes)

    at the cost of carry around one more dictionary as private state on the Axes.

  19. lpsinger commented on Feb 2, 2021

    @lpsinger
    Contributor

    See #19438 which I think gets everything we want:

    • no hashing of kwargs
    • multiple calls with matching (and no) kwargs return the same Axes object
    • multiple call with different kwargs create new axes (and continue to implicitly delete fully over-lapped axes)

    at the cost of carry around one more dictionary as private state on the Axes.

    Nice! That patch is delightfully simple.

  20. astrofrog commented on Feb 2, 2021

    @astrofrog
    ContributorAuthor

    Thanks everyone for converging on a solution and @tacaswell for the PR! :)

  21. added 2 commits that reference this issue on Feb 2, 2021
    907311c
    2960490
  22. added a commit that references this issue on Feb 9, 2021
    d95074c
  23. added 6 commits that reference this issue on Feb 11, 2021
    5b95065
    8f8cf4e
    14c8394
    337dc38
    e1987ce
    d854e5a
  24. added a commit that references this issue on Mar 8, 2021
    c30b5a7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions