-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Units handling different with plot than other functions... #9713
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
Comments
Crossref #9336 |
@story645 Here is what I found most confusing: that we bother setting the axis units at all. We set the x-axis units, but then the next call to plot ignores whatever it is set to and re-translates the new values for x into floats using whatever the registry says to use. I guess thats good so we can plot different types of date objects on the same axis. But then why set the x-axis to have certain units? Consider import pandas
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime
# import pandas.plotting
base = datetime.datetime.today()
time = [base - datetime.timedelta(days=x) for x in range(0, 3)]
data = np.random.rand( 3)
fig, ax = plt.subplots(tight_layout=True)
ax.plot(time, data)
print('xaxis converter: ',ax.xaxis.converter)
ax.plot(['a', 'b'], [1., 2.])
print('xaxis converter: ',ax.xaxis.converter)
plt.show() It yields a nonsense plot and
If I don't
|
I think that's the point @anntzer was also making that once units are set, all plots on that axis should always be set. |
(including when we are using the implicit, "pass-through" scalar converter -- I think this should disable setting another converter) |
That’d make a lot more sense modolo maybe always being able to plot an array of floats. If you want to mix types, either your converter can handle all the types you want to mix or you do the conversion by hand. I think the only hang up is the converter gets called a bunch of times that don’t have real data in them. |
As above, We could:
I really don't understand the code in matplotlib/lib/matplotlib/axes/_base.py Line 1981 in 1ca3d1a
|
Here is another test based on our Quantities test in import matplotlib.pyplot as plt
import matplotlib.units as munits
import numpy as np
import matplotlib as mpl
import xarray as xr
class Quantity:
def __init__(self, data, units):
self.magnitude = data
self.units = units
def to(self, new_units):
factors = {('hours', 'seconds'): 3600, ('minutes', 'hours'): 1 / 60,
('minutes', 'seconds'): 60, ('feet', 'miles'): 1 / 5280.,
('feet', 'inches'): 12, ('miles', 'inches'): 12 * 5280}
if self.units != new_units:
mult = factors[self.units, new_units]
return Quantity(mult * self.magnitude, new_units)
else:
return Quantity(self.magnitude, self.units)
def __copy__(self):
return Quantity(self.magnitude, self.units)
def __getattr__(self, attr):
return getattr(self.magnitude, attr)
def __getitem__(self, item):
if np.iterable(self.magnitude):
return Quantity(self.magnitude[item], self.units)
else:
return Quantity(self.magnitude, self.units)
def __array__(self):
return np.asarray(self.magnitude)
class quantity_converter(munits.ConversionInterface):
def convert(value, unit, axis):
if hasattr(value, 'units'):
return value.to(unit).magnitude
elif np.iterable(value):
try:
return [v.to(unit).magnitude for v in value]
except AttributeError:
return [Quantity(v, axis.get_units()).to(unit).magnitude
for v in value]
else:
return Quantity(value, axis.get_units()).to(unit).magnitude
def default_units(value, axis):
if hasattr(value, 'units'):
return value.units
elif np.iterable(value):
for v in value:
if hasattr(v, 'units'):
return v.units
return None
munits.registry[Quantity] = quantity_converter
# Simple test
y = Quantity(np.linspace(0, 30), 'miles')
x = Quantity(np.linspace(0, 5), 'hours')
fig, axs = plt.subplots(2, 1)
ax = axs[0]
ax.plot(x, y, 'd', color='tab:blue')
ax.yaxis.set_units('inches')
ax.xaxis.set_units('seconds')
ax.set_xlim([0, 17500])
ax.set_ylim([0, 2e6])
ax = axs[1]
ax.scatter(x, y)
ax.yaxis.set_units('inches')
ax.xaxis.set_units('seconds')
ax.set_xlim([0, 17500])
ax.set_ylim([0, 2e6])
plt.show() |
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help! |
This is probably something for @ksunden? |
Resolving this kind of discrepancy in the API is one of the overarching goals of the matplotlib/data-prototype work. Resolving in the shorter term is likely a challenge to do without unintended side-effects. At its crux, I think this is due to some artists (scatter in particular, though I believe there are several others) which use the |
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help! |
Bug report
Units always get reset for
plot
whereasscatter
(for instance) doesn't reset units...Code for reproduction
This leads to inconsistent behaviour between
plot
and (for instance)scatter
:Actual outcome
Expected outcome
The fact that neither case makes sense is maybe the fault of
basic_units.py
. But that asside, the fact that they are wrong in different ways is becauseplot
andscatter
handle units differently.There is a discussion here: #9705 (comment) but basically
plot
updates units no matter if the units have already been set for an axis, whereasscatter
and other functions do not updat ethe units if they have already been set.This was the fundamental cause of the error in #9705 not being caught.
I think
plot
should be made consistent w/ the other plotting functions and only update the units if they have not been set.I think future unit handlers should decide what to do if the new axis has different units.
Matplotlib version
print(matplotlib.get_backend())
):The text was updated successfully, but these errors were encountered: