-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Improve error messages for unit conversion #13005
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
Improve error messages for unit conversion #13005
Conversation
lib/matplotlib/category.py
Outdated
""" | ||
if unit is None: | ||
raise ValueError( | ||
'Missing unit for StrCategoryConverter. This might be caused' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what's missing here is the mapping from category to float. Worried about using unit in a user facing error because unit is a kwarg that doesn't have to be passed in for categoricals to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worried about using unit in a user facing error because unit is a kwarg that doesn't have to be passed in for categoricals to work.
That's why I'm casting the error message to "Failed to convert..." using raise ... from
.
So what's missing here is the mapping from category to float.
Not sure what that means. Moving the example from pyplot to ax to make it a little more obvious:
ax = plt.gca()
ax.bar([1, 2, 3], [50, 10, 20])
ax.set_xticks(['banana', 'apple', 'pear'])
We are trying to string positions (not labels) to a float axis. IMHO there's nothing missing here and this should error out. Ideally, the error message would be something like "Attemtping to set categorical ticks to a non-categorical axis".
But there's no good place to raise that.
The original stacktrace before this PR was:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-2-13c713392566> in <module>()
2 ax = plt.gca()
3 ax.bar([1, 2, 3], [50, 10, 20])
----> 4 ax.set_xticks(['banana', 'apple', 'pear'])
~/dev/matplotlib/lib/matplotlib/axes/_base.py in set_xticks(self, ticks, minor)
3303 Default is ``False``.
3304 """
-> 3305 ret = self.xaxis.set_ticks(ticks, minor=minor)
3306 self.stale = True
3307 return ret
~/dev/matplotlib/lib/matplotlib/axis.py in set_ticks(self, ticks, minor)
1686 """
1687 # XXX if the user changes units, the information will be lost here
-> 1688 ticks = self.convert_units(ticks)
1689 if len(ticks) > 1:
1690 xleft, xright = self.get_view_interval()
~/dev/matplotlib/lib/matplotlib/axis.py in convert_units(self, x)
1488 return x
1489
-> 1490 ret = self.converter.convert(x, self.units, self)
1491 return ret
1492
~/dev/matplotlib/lib/matplotlib/category.py in convert(value, unit, axis)
51
52 # force an update so it also does type checking
---> 53 unit.update(values)
54
55 str2idx = np.vectorize(unit._mapping.__getitem__,
AttributeError: 'NoneType' object has no attribute 'update'
So, you can raise
- in
set_ticks(self, ticks, minor)
Advantage: You know you are setting ticks, so you could issue "Attemtping to set categorical ticks to a non-categorical axis".
Disadvantage: You need to explicitly check ticks and the unit of Axis. - in
convert_units(self, x)
Advantage: You can investigate self.units and generate a more appropriate message related to categoricals.
Disadvantage: You don't know that you are setting ticks here. You would have to explicitly check which converter you are using. - in
convert(value, unit, axis)
Advantage: You know that unit must not be None so the check is trivial.
Disadvantage: You don't know anything about categorical axes anymore and therefore can only state that unit is missing and add a guess why that might be the case.
In that situation I decided to raise on 3. add add context information in 2. using the raise .. from
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not against the error, I worry that the specific word unit
will lead users down a google rabbit hole that's misguided. Basically, there's an xunit/yunit kwarg:
https://matplotlib.org/gallery/units/bar_demo2.html#sphx-glr-gallery-units-bar-demo2-py
axs[1, 0].bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm)
So I worry an error message with the word unit
might make users think the fix is to pass in something, when really the issue is that the axis input data is not a string so the axis is not registered as string units. I wonder if the following is enough:
'Axis does not support StrCategoryConvertor; axis data values must be strings'
Like you, I want to help people catch that the problem is the mismatch between the plot data and the tick data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rephrased to
raise ValueError(
'Missing category information for StrCategoryConverter; '
'this might be caused by unintendedly mixing categorical and '
'numeric data')
I don't want to mention an Axis as we don't know the call context here. In the end, this is also why we can only guess the reason for the failure but not make a clear statement what to change.
lib/matplotlib/units.py
Outdated
@@ -49,6 +49,10 @@ def default_units(x, axis): | |||
from matplotlib import cbook | |||
|
|||
|
|||
class ConversionError(ValueError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit bikesheddy but it looks more like a TypeError than a ValueError to me.
lib/matplotlib/category.py
Outdated
if unit is None: | ||
raise ValueError( | ||
'Missing unit for StrCategoryConverter. This might be caused' | ||
'by unintendedly mixing categorical and numeric data.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor preference for error messages that are single sentence and without a final dot, as that's what we mostly have in the codebase (so replace the middle dot by a semicolon).
Also a missing space after "caused".
387c345
to
83a3631
Compare
4cebc36
to
e38410c
Compare
Travis CI failure is due to #13137 and unrelated to this PR. By me, this is ready to go in. |
@@ -23,24 +23,30 @@ | |||
class StrCategoryConverter(units.ConversionInterface): | |||
@staticmethod | |||
def convert(value, unit, axis): | |||
"""Converts strings in value to floats using | |||
"""Convert strings in value to floats using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just inherit the docstring from units.ConversionInterface perhaps? (and possibly edit that docstring).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can reasonably use an inherited docstring here. Using the conversion interface for string-index mapping is a bit of a stretch and justifies explicit description.
- type of
value
is a string, which is rather the exception for ConversionInterface - mark
unit
asUnitData
- note that axis is unused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
e38410c
to
c9a0ac7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
anyone can merge (including tim) when ci passes.
Self-merging based on the reviews. The Travis CI failure is still due to #13137 (unrelated to this PR). |
Re-milestoned to 3.1 so we do not have to deal with backporting the |
PR Summary
Fixes #12990. The call stack is a bit lengthy, but that's because the error happens several levels down and at least the reason gets clear.
Assigning categoricals to floats.
results in
Assigning categoricals to dates.
results in