Skip to content

Date in matplotlib conversion does not handle "YYYY-MM-DD" format for xarray=0.21.1 #6263

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

Open
imcslatte opened this issue Feb 10, 2022 · 3 comments

Comments

@imcslatte
Copy link

What happened?

When setting axis limits is in matplotlib.:

ax.set_xlim(["2002-01-03","2002-01-20"])

A conversion error is raised.


IndexError Traceback (most recent call last)
File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\axis.py:1507, in Axis.convert_units(self, x)
1506 try:
-> 1507 ret = self.converter.convert(x, self.units, self)
1508 except Exception as e:

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\dates.py:1921, in _SwitchableDateConverter.convert(self, *args, **kwargs)
1920 def convert(self, *args, **kwargs):
-> 1921 return self._get_converter().convert(*args, **kwargs)

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\dates.py:1850, in DateConverter.convert(value, unit, axis)
1844 """
1845 If value is not already a number or sequence of numbers, convert it
1846 with date2num.
1847
1848 The unit and axis arguments are not used.
1849 """
-> 1850 return date2num(value)

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\dates.py:443, in date2num(d)
441 return d
--> 443 tzi = getattr(d[0], 'tzinfo', None)
444 if tzi is not None:
445 # make datetime naive:

IndexError: too many indices for array: array is 0-dimensional, but 1 were indexed

The above exception was the direct cause of the following exception:

ConversionError Traceback (most recent call last)
Input In [4], in
10 fig, ax = plt.subplots()
11 ax.plot(times, y)
---> 12 ax.set_xlim(["2002-01-03","2002-01-20"])

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\axes_base.py:3689, in _AxesBase.set_xlim(self, left, right, emit, auto, xmin, xmax)
3686 right = xmax
3688 self._process_unit_info([("x", (left, right))], convert=False)
-> 3689 left = self._validate_converted_limits(left, self.convert_xunits)
3690 right = self._validate_converted_limits(right, self.convert_xunits)
3692 if left is None or right is None:
3693 # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
3694 # so only grab the limits if we really need them.

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\axes_base.py:3603, in _AxesBase._validate_converted_limits(self, limit, convert)
3592 """
3593 Raise ValueError if converted limits are non-finite.
3594
(...)
3599 The limit value after call to convert(), or None if limit is None.
3600 """
3601 if limit is not None:
-> 3603 converted_limit = convert(limit)
3604 if (isinstance(converted_limit, Real)
3605 and not np.isfinite(converted_limit)):
3606 raise ValueError("Axis limits cannot be NaN or Inf")

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\artist.py:252, in Artist.convert_xunits(self, x)
250 if ax is None or ax.xaxis is None:
251 return x
--> 252 return ax.xaxis.convert_units(x)

File C:\work\miniconda3\envs\XROMStest\lib\site-packages\matplotlib\axis.py:1509, in Axis.convert_units(self, x)
1507 ret = self.converter.convert(x, self.units, self)
1508 except Exception as e:
-> 1509 raise munits.ConversionError('Failed to convert value(s) to axis '
1510 f'units: {x!r}') from e
1511 return ret

ConversionError: Failed to convert value(s) to axis units: '2002-01-03'

When we downgrade to xarray=0.20..2. axis limits are changed as expected.

What did you expect to happen?

ax.set_xlim should interpert dates of format "YYYY-MM-DD" correctly and scale the xaxis appropriately.

Minimal Complete Verifiable Example

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt

times = np.arange(np.datetime64('2001-01-02'),
                      np.datetime64('2002-02-03'), np.timedelta64(75, 'm'))
y = np.random.randn(len(times))


fig, ax = plt.subplots()
ax.plot(times, y)
ax.set_xlim(["2002-01-03","2002-01-20"])

Relevant log output

No response

Anything else we need to know?

It appears that matplotlib uses a pandas date converter when using xarray 0.20.2 is imported but not for xarray 0.21.1.

Environment

INSTALLED VERSIONS

commit: None
python: 3.8.12 | packaged by conda-forge | (default, Oct 12 2021, 21:22:46) [MSC v.1916 64 bit (AMD64)]
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 140 Stepping 1, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: ('English_United States', '1252')
libhdf5: 1.12.1
libnetcdf: 4.8.1

xarray: 0.21.1
pandas: 1.4.0
numpy: 1.21.5
scipy: 1.8.0
netCDF4: 1.5.8
pydap: None
h5netcdf: None
h5py: None
Nio: None
zarr: 2.11.0
cftime: 1.5.2
nc_time_axis: None
PseudoNetCDF: None
rasterio: None
cfgrib: None
iris: None
bottleneck: None
dask: 2022.01.1
distributed: 2022.01.1
matplotlib: 3.5.1
cartopy: 0.20.2
seaborn: None
numbagg: None
fsspec: 2022.01.0
cupy: None
pint: None
sparse: None
setuptools: 59.8.0
pip: 22.0.3
conda: None
pytest: None
IPython: 8.0.1
sphinx: None

@imcslatte imcslatte added bug needs triage Issue that has not been reviewed by xarray team member labels Feb 10, 2022
@Illviljan
Copy link
Contributor

xarray now relies on matplotlibs converters instead of automatically registering pandas converters, see #6109.

A pure matplotlib version doesn't work either so importing xarray shouldn't all of a sudden change that:

import numpy as np
import matplotlib.pyplot as plt

times = np.arange(np.datetime64('2001-01-02'),
                      np.datetime64('2002-02-03'), np.timedelta64(75, 'm'))
y = np.random.randn(len(times))

fig, ax = plt.subplots()
ax.plot(times, y)
ax.set_xlim(["2002-01-03","2002-01-20"])

One way is to use datetime64 in set_xlim, which makes sense to me since times is datetime64 as well:

import numpy as np
import matplotlib.pyplot as plt

times = np.arange(np.datetime64('2001-01-02'),
                      np.datetime64('2002-02-03'), np.timedelta64(75, 'm'))
y = np.random.randn(len(times))

fig, ax = plt.subplots()
ax.plot(times, y)
ax.set_xlim(np.array(["2002-01-03","2002-01-20"], dtype="datetime64"))

Or use pandas converters like xarray did before:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

pd.plotting.register_matplotlib_converters()

times = np.arange(np.datetime64('2001-01-02'),
                      np.datetime64('2002-02-03'), np.timedelta64(75, 'm'))
y = np.random.randn(len(times))

fig, ax = plt.subplots()
ax.plot(times, y)
ax.set_xlim(["2002-01-03","2002-01-20"])

@mathause mathause added topic-plotting usage question and removed bug needs triage Issue that has not been reviewed by xarray team member labels Feb 21, 2022
@dcherian
Copy link
Contributor

@jklymak it seems a benefit of the pandas converter is that it converts strings to dates. Can the matplotlib converter do the same?

@jklymak
Copy link
Contributor

jklymak commented Feb 23, 2022

It could, after the units are set to dates, but all it would do is pass to datetime64, so the recommendation would be that users do that explicitly.

If the units are not set to dates (ie. this is the first call on the axis) then strings are interpreted as categories in Matplotlib, and all sorts of hilarity ensues if the strings are all dates....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants