Skip to content

update units.ConversionInterface to have to_numeric and from_numeric #16922

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

terencehonles
Copy link
Contributor

This change updates matplotlib.units.ConversionInterface to have a to_numeric instead of a convert method, and adds a from_numeric method. The method convert is renamed to to_numeric and similarly named methods are updated accordingly. The renaming is done to make convert less ambiguous with a forward and backward conversion on the ConversionInterface.

Adding a from_numeric method standardizes the way to convert back to a native object from an internal numeric value. This allows registered converters to provide more specific conversions which will allow formatters and locators to provide an implementation which is not as tied to the internal values Matplotlib uses.

PR Summary

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

@terencehonles terencehonles force-pushed the add-from-numeric-conversion-to-ConversionInterface branch 2 times, most recently from 5ccae25 to 80087a2 Compare March 26, 2020 18:00
@story645
Copy link
Member

So this is a major overhaul that might create deprecation issues with the third party packages the conversation interface is aimed at. Is there buy in/packages that need this functionality? And can it be achieved w/o renaming convert?

@terencehonles terencehonles force-pushed the add-from-numeric-conversion-to-ConversionInterface branch from 80087a2 to 17c0e26 Compare March 26, 2020 18:35
@terencehonles
Copy link
Contributor Author

So this is a major overhaul that might create deprecation issues with the third party packages the conversation interface is aimed at. Is there buy in/packages that need this functionality? And can it be achieved w/o renaming convert?

Thanks for the quick response, in this PR as proposed I have marked convert as a pending deprecation (because I think this conversation is needed, and possibly with packages that implement the interface). I have made everywhere that previously called convert backwards compatible and it would be up to Matplotlib to determine the deprecation timeline. So other than raising warnings, this change should not affect implementers of the ConversionInterface, and it would be safe to provide both convert and to_numeric.

Regarding the buy in, I am not sure, but I believe this change is needed because pandas uses a different date converter. Pandas does subclass the AutoDate{Formatter,Locator} (see pandas) and it provides those in its AxisInfo, but when using the AutoDate{Formatter,Locator} provided by Matplotlib (which would seem like it should work) or the ones provided by pandas I'm getting an exception ValueError: year 4320917 is out of range. When debugging the data, the data is a valid UNIX timestamp, but not a valid ordinal, and is caused by the DateLocator calling num2date when the value needs to be converted into an object, but since the DateLocator has no way to determine what the value was before it converts it incorrectly.

I believe my issue is related to pandas-dev/pandas#18344, and now that I have prepared this PR, I will be trying to make sure the tests pass, and testing my code with a patch to pandas that implements a from_numeric.

Regarding the name change, I believe this could be made without a name change, but it sacrifices the readability and makes the functions slightly ambiguous. Arguably you could make the methods be convert and unconvert, but which direction are they converting? Without reading the documentation it feels likely that someone might pick the wrong method, and I believe it would be better to start transitioning away from an ambiguous name, but potentially always leaving this a pending deprecation and falling back to the old method.

@terencehonles terencehonles force-pushed the add-from-numeric-conversion-to-ConversionInterface branch from 17c0e26 to 408ecb6 Compare March 26, 2020 19:14
Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Thanks @terencehonles. This is rather a big step in the API, which will have to be thought through carefully. Please be prepared, that the review process may take some time. While I haven't looked into the details yet, the basic idea looks reasonable.

Apart from the individual comments, please address the following:

  • Change all deprecations to 3.3, because 3.2 is already out.
  • Make all deprecations pending. While you wrote you did in the PR comment, I don't see it in the code.
  • This needs an API change note at doc/api/next_api_changes.rst for the deprecations. The new to and from methods should also be announced, probably best at doc/users/next_whats_new.

@@ -51,7 +51,7 @@ def default_units(x, axis):
from matplotlib import cbook


class ConversionError(TypeError):
class ConversionError(ValueError):
Copy link
Member

Choose a reason for hiding this comment

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

This would be an API change. Not sure it's needed or even desirable. It's raised when convert_units(x) cannot process the argument. Typically that should happen rather if x has an unsupported type than value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking at where it is used

try:
ret = self.converter.convert(x, self.units, self)
except Exception as e:
raise munits.ConversionError('Failed to convert value(s) to axis '
f'units: {x!r}') from e

I personally would have thought that should have been a ValueError, but I can understand why in some cases you can argue one way or another for ValueError vs TypeError. I do agree this would be a breaking change if someone is expecting to catch a TypeError instead of a ConversionError and maybe you feel strongly enough that this shouldn't change. This is partially changed because I'm more under the impression it should be a ValueError, and I did end up using the exception a couple more places and throwing this instead of a ValueError. The appropriate fix may be creating two different errors or maybe just documenting an API change.

So in summary: I did this because it didn't seem like it would be a big deal and I am using the exception more widely than it was used before (new use cases with the new method I added, and for existing errors that were both ValueErrors and seemed like they should have actually fallen under a ConversionError)

tests.py Outdated
@@ -1,29 +0,0 @@
#!/usr/bin/env python
Copy link
Member

Choose a reason for hiding this comment

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

Please do not add unrelated changes to large PRs like this one.

Apart from whether this should be removed or not, any unrelated change makes reviewing harder, which may delay the review process, or even discourage reviewers from taking on the PR.

Instead, please keep the PR as focused as possible and submit everything else as a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These changes were simply to allow me to more easily test my changes (other than using docker, I only have Python 2.7 and 3.8 on my system). I created PR #16986 which cherry picks these changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I guess this comment more belongs on tox.ini the change on this file was that I wasted a lot of time trying to figure why this test failed, and I didn't realize there was a diff file that was generated. I updated this test util to make it more obvious, but I can also pull this out to a different PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this comment is on a deleted file, I will pull this and the change I was talking about ^^^ into a different PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It also looks like I was wrong about deleting this file. It looks like it expects you have already installed matplotlib in pip editable mode, and I'll suggest that change instead of deleting this file (I looked at its commit log and it has been updated recently so I looked a bit closer)

@terencehonles terencehonles force-pushed the add-from-numeric-conversion-to-ConversionInterface branch from 408ecb6 to e170f99 Compare March 31, 2020 20:42
This change updates `matplotlib.units.ConversionInterface` to have a
`to_numeric` instead of a `convert` method, and adds a `from_numeric`
method. The method `convert` is renamed to `to_numeric` and similarly
named methods are updated accordingly. The renaming is done to make
`convert` less ambiguous with a forward and backward conversion on the
`ConversionInterface`.

Adding a `from_numeric` method standardizes the way to convert back to a
native object from an internal numeric value. This allows registered
converters to provide more specific conversions which will allow
formatters and locators to provide an implementation which is not as
tied to the internal values Matplotlib uses.
@terencehonles terencehonles force-pushed the add-from-numeric-conversion-to-ConversionInterface branch from e170f99 to 2fe4da5 Compare March 31, 2020 21:02
@jklymak
Copy link
Member

jklymak commented Mar 31, 2020

There have been lots of discussion about fixing units in Matplotlib, including the problem of calculating the inverse. Currently my understanding is that there is a new data model under development that will address this by @tacaswell and @story645. How does this PR fit into those plans? I’m also not in favour of renaming a long-standing method for purely aesthetic reasons.

@terencehonles terencehonles mentioned this pull request Mar 31, 2020
6 tasks
@terencehonles
Copy link
Contributor Author

There have been lots of discussion about fixing units in Matplotlib, including the problem of calculating the inverse. Currently my understanding is that there is a new data model under development that will address this by @tacaswell and @story645. How does this PR fit into those plans? I’m also not in favour of renaming a long-standing method for purely aesthetic reasons.

I'm not aware of those plans, I can possibly help with those plans if needed. I was mainly taking a stab at this because it looks like it has been a longstanding issue and it affects me directly. So rather than just patching it internally or updating pandas's formatters/locators I figured something more generic and less surprising would be ideal. If there is an email thread/PR/issue already I can be added to it.

@terencehonles
Copy link
Contributor Author

Thanks @terencehonles. This is rather a big step in the API, which will have to be thought through carefully. Please be prepared, that the review process may take some time. While I haven't looked into the details yet, the basic idea looks reasonable.

Apart from the individual comments, please address the following:

  • Change all deprecations to 3.3, because 3.2 is already out.
  • Make all deprecations pending. While you wrote you did in the PR comment, I don't see it in the code.
  • This needs an API change note at doc/api/next_api_changes.rst for the deprecations. The new to and from methods should also be announced, probably best at doc/users/next_whats_new.

I updated the deprecations, the pending flag was only on the conversion interface, but I added it to all of the deprecations I added in this PR. Regarding the documentation I figured starting a discussion about the change would be appropriate before spending any more time than I have.

I appreciate you looking at it and @jklymak and any others that might have the chance to offer some input.

@jklymak
Copy link
Member

jklymak commented Mar 31, 2020

I'm not aware of those plans, I can possibly help with those plans if needed

I'm sure that would be appreciated.

https://github.com/matplotlib/matplotlib/labels/units

Note that there are other solutions to pandas-dev/pandas#18344 under https://github.com/matplotlib/matplotlib/labels/Date%20handling, in particular #15008

@tacaswell tacaswell added this to the v3.3.0 milestone Apr 6, 2020
@tacaswell
Copy link
Member

The data model work is going to intersect with the units work by better managing the life cycle of when it gets called, but should be relatively agnostic to how we spell the unit conversion.

@tacaswell
Copy link
Member

We talked about this on the dev-call this week (.https://hackmd.io/-fJ2hZcJQz2gfbqzabrZMQ?view#Units-change). We have several concerns:

  • the renames are making in very hard to think about and review this set of changes. While we appreciate you work on this, can you back the deprecation and rename of convert out of this PR if you want to go forward with this. I think the same goes for the staticmethod -> classmethod changes.
  • in general, it is not clear that the "unconvert" is always well defined. For example if the user gives us both np.datetime64 or datetime.datetime we lose the information about which one we started as so don't know how to go back to what the user provided us. If this is going to go in the unconvert method is going to have to be optional for over and provide a way for the the converter to declare that it can't be inverted.

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

Please:

  • revert the rename API changes, we can discuss those in a separate PR
  • address concerns about converters that are not actually invertable.

@tacaswell tacaswell modified the milestones: v3.5.0, v3.6.0 Aug 5, 2021
@timhoffm timhoffm modified the milestones: v3.6.0, unassigned Apr 30, 2022
@story645 story645 modified the milestones: unassigned, needs sorting Oct 6, 2022
@github-actions
Copy link

Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you need a review or guidance to move the PR forward! If you do not plan on continuing the work, please let us know so that we can either find someone to take the PR over, or close it.

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Jul 17, 2023
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.

7 participants