8000 Add "standard" Axes wrapper getters/setters for Axis invertedness. by anntzer · Pull Request #29074 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Add "standard" Axes wrapper getters/setters for Axis invertedness. #29074

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
Nov 12, 2024

Conversation

anntzer
Copy link
Contributor
@anntzer anntzer commented Nov 5, 2024

Currently, toggling an axis invertedness requires either using the Axes method ax.invert_xaxis() (whose effect depends on whether it has already been called before) or going through the axis method ax.xaxis.set_inverted(); likewise, querying invertedness state requires either using the ax.xaxis_inverted() method, which has slightly nonstandard naming, or going through the axis method ax.xaxis.get_inverted().

For practicality, provide getters and setters with standard names: ax.get_xinverted()/ax.set_xinverted() (and likewise for y/z). In particular, the "standard" setter can be used with the multi-setter Artist.set(), or directly when creating the axes
(add_subplot(xinverted=True)).

PR summary

PR checklist

@anntzer anntzer force-pushed the si branch 2 times, most recently from f5764c9 to 3c6eca7 Compare November 5, 2024 12:41
@github-actions github-actions bot added the Documentation: API files in lib/ and doc/api label Nov 5, 2024
@timhoffm
Copy link
Member
timhoffm commented Nov 5, 2024

My 2 cents:

  • Generally, I advocate to not replicate all Axis methods on the Axes. We intentionally made the properties xaxis / yaxis available to have an alomost as short access path ax.xaxis.get_inverted() vs. ax.get_xinverted(). So if inversion was not yet available on Axes level, I would not add it.
  • The existing functions are invert_xaxis() / xaxis_inverted() are unfortunate. They do not follow the standard get/set naming patterns and it's not clear (neither from docstring nor from documentation that invert_xaxis() is a toggle, rather than setting the inversion unconditionally.
  • While the naming set_xinverted formally conforms to the pattern of pulling Axis methods up, it feels slightly terse without having "axis" in the name.

In the spirit of API consistency, we should move away from the old functions. This means at least discouraging. I'm unsure on deprecation. As an replacement, I'd be fine with having inversion only available on the axis and thus reduce the Axes API footprint. This would mean you don't get the standard axes property benefits like add_subplot(xinverted=True)- you don't have them currently, so it's at least not worsening the API. I assume axis inversion is not used too often (note also that pyplot does not have an function for it, which may be a supporting point for not beeing used too often).

But if you feel strongly, that we should pull this up into the Axes API, I'd be ok with it as well.

@anntzer
Copy link
Contributor Author
anntzer commented Nov 5, 2024

FWIW I do ax = plt.figure(...).add_subplot(..., aspect=1); ax.yaxis.set(inverted=True) very often (for plotting things (e.g. particle trajectories) where coordinates would match image coordinates but without having an imshow() call that would implicitly do the set_aspect(1); invert_yaxis() dance for me, and this is why I propose to make add_subplot(..., aspect=1, yinverted=True) possible.

(Yes, I could write ax.invert_yaxis() instead of ax.yaxis.set(inverted=True) but I don't like it for the reasons already discussed.)

@jklymak
Copy link
Member
jklymak commented Nov 5, 2024

I was going to say that images invert the axis all the time. Also in my field, we plot depth on an inverted axis all the time. Just to say that I don't think inverting an axis is very rare. I think the proposed changes here are straight forward enough that they are OK from an API point of view - I agree that its not nice to have many ways to do the same thing, but in this case "the same thing" is pretty straight forward and well-defined.

@timhoffm
Copy link
Member
timhoffm commented Nov 5, 2024

Semi-OT: Reason why ax.invert_xaxis() is odd: By itself this is a valid name (a combination verb_noun), and we have legitimate other names with that pattern, e.g. Axes.update_datalim. The point here is though, that we don't regard inversion of an axis an operation; it's rather a state ("is the axis inverted or not"). While one can descripe every change of state as an operation (inverting results in an inverted state), states are more specific in that they can also be queried. Therefore the state associated set/get methods are more appropriate than an operation method.

Note: invert_xaxis() is the only case of this kind of pattern in Axes. Getting rid of it makes the API more consistent. For reference, there are other oddballs (not suggesting to do something about it):

  • margins(), grid(), tick_params(), locator_params() - these also set state and should be set_*methods.
  • xaxis_date() / yaxis_date() - these are weirdly specific

@timhoffm
Copy link
Member
timhoffm commented Nov 5, 2024

@anntzer can you please discourage use of the old syntax?

  • add
          .. admonition:: Discouraged
          
             The use of this method is discouraged.
             Use `.Axes.set_xinverted` instead.
    
    to the old functions
  • mention the preference for the new functions in the what's new entry.

@rcomer
Copy link
Member
rcomer commented Nov 5, 2024

An inverted y-axis is also common in atmospheric science, when we plot against pressure.

@anntzer
Copy link
Contributor Author
anntzer commented Nov 7, 2024

Edited as suggested by @timhoffm.
FWIW I suspect that the name of tick_params() / locator_params() comes from the coresponding pyplot functions, which were then directly transposed as axes methods. Note that there is a corresponding set_tick_params() axis method (but no set_locator_params() likely because one can easily directly access the locator itself and call set_params() on it).

@timhoffm
Copy link
Member
timhoffm commented Nov 7, 2024

Logically 👍

You need to fix the linting errors. And I don't know what to make of the failing tests. Somehow 3d stuff seems broken.

@timhoffm
Copy link
Member
timhoffm commented Nov 7, 2024

Doc build failures are real (reference issues related to the PR).

Currently, toggling an axis invertedness requires either using the
Axes method `ax.invert_xaxis()` (whose effect depends on whether it
has already been called before) or going through the axis method
`ax.xaxis.set_inverted()`; likewise, querying invertedness state
requires either using the `ax.xaxis_inverted()` method, which has
slightly nonstandard naming, or going through the axis method
`ax.xaxis.get_inverted()`.

For practicality, provide getters and setters with standard names:
`ax.get_xinverted()`/`ax.set_xinverted()` (and likewise for y/z).  In
particular, the "standard" setter can be used with the multi-setter
Artist.set(), or directly when creating the axes
(`add_subplot(xinverted=True)`).
xaxis_inverted = _axis_method_wrapper("xaxis", "get_inverted")
if xaxis_inverted.__doc__:
Copy link
Member

Choose a reason for hiding this comment

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

Why is this necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

__doc__ can be unset in "optimized" mode (https://docs.python.org/3/using/cmdline.html#cmdoption-OO). There's already a few other places in the codebase with the same check.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, still not following what this does or why... Is this because we are building __doc__ dynamically?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, in optimized mode the following line `xaxis_inverted.doc += …” would raise and therefore must be guarded.

@jklymak jklymak merged commit 1c4a554 into matplotlib:main Nov 12, 2024
43 checks passed
@anntzer anntzer deleted the si branch November 12, 2024 16:23
@wuyao1997
Copy link
Contributor

Hello, I noticed in the Highlights of v3.10.0 it mentions Standard getters/setters for axis inversion state. However, on my Windows machine, I am actually unable to access the matplotlib.axes.Axes.get_xinverted function in matplotlib 3.10.0, even though this function is present in the web documentation for the dev version. I could only find this related Pull Request. Could it be that this PR was not merged into v3.10.0?

image

@QuLogic
Copy link
Member
QuLogic commented Dec 16, 2024

@ksunden that should not be in the release notes; this change landed far too late for 3.10, and wasn't backported.

@QuLogic QuLogic added this to the v3.11.0 milestone Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0