-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Apply unit conversion early in errorbar(). #19526
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
Conversation
698f065
to
cc2517c
Compare
Probably these changes should be repeated in |
Let's merge the two PRs as separate improvements and figure out how to reuse the logic in both cases later? I actually already have a followup patch for the 2D case that factors together the x and y handling, probably I can make it handle z too. |
lib/matplotlib/axes/_axes.py
Outdated
x, y, xerr, yerr = self._process_unit_info( | ||
[("x", x), ("y", y), ("x", xerr), ("y", yerr)], kwargs) |
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 now convert=True
, but I seem to be missing where the old conversion was that would be removed by this change?
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 think we just used to propagate unitful data to Line2D and rely on Line2D being (the only artist) able to handle them.
This breaks mixed unit error bars like: x = np.arange(10)
y = [datetime(2020, 5, i * 2 + 1) for i in x]
plt.errorbar(x, y, timedelta(days=0.5)) |
Ooops, good catch! |
This allow using normal numpy constructs rather than manually looping and broadcasting. _process_unit_info was already special-handling `data is None` in a few places; the change here only handle the (theoretical) extra case where a custom unit converter would fail to properly pass None through.
cc2517c
to
8cd22b4
Compare
Good catch, now fixed by using object arrays whenever the inputs are not already arrays (test included). |
I think this has broken |
I'll have a look. |
Looks like the following fixes the problem: diff --git i/lib/matplotlib/axes/_axes.py w/lib/matplotlib/axes/_axes.py
index e906dba6c8..e9e8c3ef25 100644
--- i/lib/matplotlib/axes/_axes.py
+++ w/lib/matplotlib/axes/_axes.py
@@ -3427,13 +3427,17 @@ class Axes(_AxesBase):
the note in the main docstring about this parameter's name.
"""
try:
- low, high = np.broadcast_to(err, (2, len(data)))
+ np.broadcast_to(err, (2, len(data)))
except ValueError:
raise ValueError(
f"'{name}err' (shape: {np.shape(err)}) must be a scalar "
f"or a 1D or (2, n) array-like whose shape matches "
f"'{name}' (shape: {np.shape(data)})") from None
- return data - low * ~lolims, data + high * ~uplims # low, high
+ # This is like
+ # low, high = np.broadcast_to(...)
+ # return data - low * ~lolims, data + high * ~uplims
+ # except that broadcast_to would strip units.
+ return data + np.row_stack([-(1 - lolims), 1 - uplims]) * err
if xerr is not None:
left, right = extract_err('x', xerr, x, xlolims, xuplims) but if you can help with writing a minimal test case to check that, that would be great :-) |
Here is a minimal test case from the broken import matplotlib.pyplot as plt
from astropy import units as u
from astropy.visualization.units import quantity_support
with quantity_support():
x = [1, 2, 3] * u.s
y = [1, 2, 3] * u.m
yerr = [3, 2, 1] * u.cm
fig, ax = plt.subplots()
ax.errorbar(x, y, yerr=yerr)
assert ax.xaxis.get_units() == u.s
assert ax.yaxis.get_units() == u.m
plt.show() # Only if you want to see it, not for CI |
p.s. Here is the actual implementation of |
@pllim I'm loving that astropy is testing against our default branch :) |
This allow using normal numpy constructs rather than manually looping
and broadcasting.
_process_unit_info was already special-handling
data is None
in a fewplaces; the change here only handle the (theoretical) extra case where a
custom unit converter would fail to properly pass None through.
PR Summary
PR Checklist
pytest
passes).flake8
on changed files to check).flake8-docstrings
and runflake8 --docstring-convention=all
).doc/users/next_whats_new/
(follow instructions in README.rst there).doc/api/next_api_changes/
(follow instructions in README.rst there).