Skip to content

Commit 8a1a8a1

Browse files
Julius Buseckedcherian
authored andcommitted
enable internal plotting with cftime datetime (#2665)
enable internal plotting with cftime datetime
1 parent e097763 commit 8a1a8a1

File tree

12 files changed

+120
-63
lines changed

12 files changed

+120
-63
lines changed

ci/requirements-py37-windows.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ channels:
44
dependencies:
55
- python=3.7
66
- cftime
7+
- nc-time-axis
78
- dask
89
- distributed
910
- h5py

ci/requirements-py37.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ channels:
44
dependencies:
55
- python=3.7
66
- cftime
7+
- nc-time-axis
78
- dask
89
- distributed
910
- h5py

doc/installing.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ For plotting
6161
:ref:`plot-maps`
6262
- `seaborn <https://stanford.edu/~mwaskom/software/seaborn/>`__: for better
6363
color palettes
64+
- `nc-time-axis <https://github.com/SciTools/nc-time-axis>`__: for plotting
65+
cftime.datetime objects (1.2.0 or later)
6466

6567

6668
Instructions
@@ -109,4 +111,4 @@ To run these benchmark tests in a local machine, first install
109111
- `airspeed-velocity <https://asv.readthedocs.io/en/latest/>`__: a tool for benchmarking Python packages over their lifetime.
110112

111113
and run
112-
``asv run # this will install some conda environments in ./.asv/envs``
114+
``asv run # this will install some conda environments in ./.asv/envs``

doc/plotting.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ Matplotlib syntax and function names were copied as much as possible, which
2323
makes for an easy transition between the two.
2424
Matplotlib must be installed before xarray can plot.
2525

26+
To use xarray's plotting capabilities with time coordinates containing
27+
``cftime.datetime`` objects
28+
`nc-time-axis <https://github.com/SciTools/nc-time-axis>`_ v1.2.0 or later
29+
needs to be installed.
30+
2631
For more extensive plotting applications consider the following projects:
2732

2833
- `Seaborn <http://seaborn.pydata.org/>`_: "provides
@@ -226,7 +231,7 @@ Step plots
226231
~~~~~~~~~~
227232

228233
As an alternative, also a step plot similar to matplotlib's ``plt.step`` can be
229-
made using 1D data.
234+
made using 1D data.
230235

231236
.. ipython:: python
232237
@@ -248,7 +253,7 @@ when plotting data grouped with :py:func:`xarray.Dataset.groupby_bins`.
248253
plt.ylim(-20,30)
249254
@savefig plotting_example_step_groupby.png width=4in
250255
plt.title('Zonal mean temperature')
251-
256+
252257
In this case, the actual boundaries of the bins are used and the ``where`` argument
253258
is ignored.
254259

doc/time-series.rst

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ will be used for indexing. :py:class:`~xarray.CFTimeIndex` enables a subset of
7474
the indexing functionality of a :py:class:`pandas.DatetimeIndex` and is only
7575
fully compatible with the standalone version of ``cftime`` (not the version
7676
packaged with earlier versions ``netCDF4``). See :ref:`CFTimeIndex` for more
77-
information.
77+
information.
7878

7979
Datetime indexing
8080
-----------------
@@ -215,7 +215,7 @@ For more examples of using grouped operations on a time dimension, see
215215

216216

217217
.. _CFTimeIndex:
218-
218+
219219
Non-standard calendars and dates outside the Timestamp-valid range
220220
------------------------------------------------------------------
221221

@@ -224,14 +224,14 @@ Through the standalone ``cftime`` library and a custom subclass of
224224
functionality enabled through the standard :py:class:`pandas.DatetimeIndex` for
225225
dates from non-standard calendars commonly used in climate science or dates
226226
using a standard calendar, but outside the `Timestamp-valid range`_
227-
(approximately between years 1678 and 2262).
227+
(approximately between years 1678 and 2262).
228228

229229
.. note::
230230

231231
As of xarray version 0.11, by default, :py:class:`cftime.datetime` objects
232232
will be used to represent times (either in indexes, as a
233-
:py:class:`~xarray.CFTimeIndex`, or in data arrays with dtype object) if
234-
any of the following are true:
233+
:py:class:`~xarray.CFTimeIndex`, or in data arrays with dtype object) if
234+
any of the following are true:
235235

236236
- The dates are from a non-standard calendar
237237
- Any dates are outside the Timestamp-valid range.
@@ -252,7 +252,7 @@ coordinate with dates from a no-leap calendar and a
252252
dates = [DatetimeNoLeap(year, month, 1) for year, month in
253253
product(range(1, 3), range(1, 13))]
254254
da = xr.DataArray(np.arange(24), coords=[dates], dims=['time'], name='foo')
255-
255+
256256
xarray also includes a :py:func:`~xarray.cftime_range` function, which enables
257257
creating a :py:class:`~xarray.CFTimeIndex` with regularly-spaced dates. For
258258
instance, we can create the same dates and DataArray we created above using:
@@ -261,20 +261,20 @@ instance, we can create the same dates and DataArray we created above using:
261261
262262
dates = xr.cftime_range(start='0001', periods=24, freq='MS', calendar='noleap')
263263
da = xr.DataArray(np.arange(24), coords=[dates], dims=['time'], name='foo')
264-
264+
265265
For data indexed by a :py:class:`~xarray.CFTimeIndex` xarray currently supports:
266266

267267
- `Partial datetime string indexing`_ using strictly `ISO 8601-format`_ partial
268268
datetime strings:
269-
269+
270270
.. ipython:: python
271271
272272
da.sel(time='0001')
273273
da.sel(time=slice('0001-05', '0002-02'))
274274
275275
- Access of basic datetime components via the ``dt`` accessor (in this case
276276
just "year", "month", "day", "hour", "minute", "second", "microsecond",
277-
"season", "dayofyear", and "dayofweek"):
277+
"season", "dayofyear", and "dayofweek"):
278278

279279
.. ipython:: python
280280
@@ -323,14 +323,7 @@ For data indexed by a :py:class:`~xarray.CFTimeIndex` xarray currently supports:
323323
da.resample(time='81T', closed='right', label='right', base=3).mean()
324324
325325
.. note::
326-
327-
While much of the time series functionality that is possible for standard
328-
dates has been implemented for dates from non-standard calendars, there are
329-
still some remaining important features that have yet to be implemented,
330-
for example:
331-
332-
- Built-in plotting of data with :py:class:`cftime.datetime` coordinate axes
333-
(:issue:`2164`).
326+
334327

335328
For some use-cases it may still be useful to convert from
336329
a :py:class:`~xarray.CFTimeIndex` to a :py:class:`pandas.DatetimeIndex`,
@@ -351,8 +344,8 @@ For data indexed by a :py:class:`~xarray.CFTimeIndex` xarray currently supports:
351344
do not depend on differences between dates (e.g. differentiation,
352345
interpolation, or upsampling with resample), as these could introduce subtle
353346
and silent errors due to the difference in calendar types between the dates
354-
encoded in your data and the dates stored in memory.
355-
347+
encoded in your data and the dates stored in memory.
348+
356349
.. _Timestamp-valid range: https://pandas.pydata.org/pandas-docs/stable/timeseries.html#timestamp-limitations
357350
.. _ISO 8601-format: https://en.wikipedia.org/wiki/ISO_8601
358351
.. _partial datetime string indexing: https://pandas.pydata.org/pandas-docs/stable/timeseries.html#partial-string-indexing

doc/whats-new.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Breaking changes
2424
- Remove support for Python 2. This is the first version of xarray that is
2525
Python 3 only. (:issue:`1876`).
2626
By `Joe Hamman <https://github.com/jhamman>`_.
27-
- The `compat` argument to `Dataset` and the `encoding` argument to
27+
- The `compat` argument to `Dataset` and the `encoding` argument to
2828
`DataArray` are deprecated and will be removed in a future release.
2929
(:issue:`1188`)
3030
By `Maximilian Roos <https://github.com/max-sixty>`_.
@@ -34,6 +34,10 @@ Breaking changes
3434
Enhancements
3535
~~~~~~~~~~~~
3636

37+
- Internal plotting now supports ``cftime.datetime`` objects as time series.
38+
(:issue:`2164`)
39+
By `Julius Busecke <https://github.com/jbusecke>`_ and
40+
`Spencer Clark <https://github.com/spencerkclark>`_.
3741
- Add ``data=False`` option to ``to_dict()`` methods. (:issue:`2656`)
3842
By `Ryan Abernathey <https://github.com/rabernat>`_
3943
- :py:meth:`~xarray.DataArray.coarsen` and

xarray/core/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ def resample(self, indexer=None, skipna=None, closed=None, label=None,
713713
array([ 0. , 0.032258, 0.064516, ..., 10.935484, 10.967742, 11. ])
714714
Coordinates:
715715
* time (time) datetime64[ns] 1999-12-15 1999-12-16 1999-12-17 ...
716-
716+
717717
Limit scope of upsampling method
718718
>>> da.resample(time='1D').nearest(tolerance='1D')
719719
<xarray.DataArray (time: 337)>

xarray/plot/plot.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import numpy as np
1111
import pandas as pd
1212

13-
from xarray.core.common import contains_cftime_datetimes
14-
1513
from .facetgrid import _easy_facetgrid
1614
from .utils import (
1715
_add_colorbar, _ensure_plottable, _infer_interval_breaks, _infer_xy_labels,
@@ -139,15 +137,6 @@ def plot(darray, row=None, col=None, col_wrap=None, ax=None, hue=None,
139137
"""
140138
darray = darray.squeeze()
141139

142-
if contains_cftime_datetimes(darray):
143-
raise NotImplementedError(
144-
'Built-in plotting of arrays of cftime.datetime objects or arrays '
145-
'indexed by cftime.datetime objects is currently not implemented '
146-
'within xarray. A possible workaround is to use the '
147-
'nc-time-axis package '
148-
'(https://github.com/SciTools/nc-time-axis) to convert the dates '
149-
'to a plottable type and plot your data directly with matplotlib.')
150-
151140
plot_dims = set(darray.dims)
152141
plot_dims.discard(row)
153142
plot_dims.discard(col)

xarray/plot/utils.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010

1111
from ..core.options import OPTIONS
1212
from ..core.utils import is_scalar
13+
from distutils.version import LooseVersion
14+
15+
try:
16+
import nc_time_axis
17+
if LooseVersion(nc_time_axis.__version__) < LooseVersion('1.2.0'):
18+
nc_time_axis_available = False
19+
else:
20+
nc_time_axis_available = True
21+
except ImportError:
22+
nc_time_axis_available = False
1323

1424
ROBUST_PERCENTILE = 2.0
1525

@@ -471,16 +481,29 @@ def _ensure_plottable(*args):
471481
"""
472482
numpy_types = [np.floating, np.integer, np.timedelta64, np.datetime64]
473483
other_types = [datetime]
474-
484+
try:
485+
import cftime
486+
cftime_datetime = [cftime.datetime]
487+
except ImportError:
488+
cftime_datetime = []
489+
other_types = other_types + cftime_datetime
475490
for x in args:
476491
if not (_valid_numpy_subdtype(np.array(x), numpy_types)
477492
or _valid_other_type(np.array(x), other_types)):
478493
raise TypeError('Plotting requires coordinates to be numeric '
479-
'or dates of type np.datetime64 or '
480-
'datetime.datetime or pd.Interval.')
481-
482-
483-
def _ensure_numeric(arr):
494+
'or dates of type np.datetime64, '
495+
'datetime.datetime, cftime.datetime or '
496+
'pd.Interval.')
497+
if (_valid_other_type(np.array(x), cftime_datetime)
498+
and not nc_time_axis_available):
499+
raise ImportError('Plotting of arrays of cftime.datetime '
500+
'objects or arrays indexed by '
501+
'cftime.datetime objects requires the '
502+
'optional `nc-time-axis` (v1.2.0 or later) '
503+
'package.')
504+
505+
506+
def _numeric(arr):
484507
numpy_types = [np.floating, np.integer]
485508
return _valid_numpy_subdtype(arr, numpy_types)
486509

xarray/tests/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def LooseVersion(vstring):
6363
has_pynio, requires_pynio = _importorskip('Nio')
6464
has_pseudonetcdf, requires_pseudonetcdf = _importorskip('PseudoNetCDF')
6565
has_cftime, requires_cftime = _importorskip('cftime')
66+
has_nc_time_axis, requires_nc_time_axis = _importorskip('nc_time_axis',
67+
minversion='1.2.0')
6668
has_cftime_1_0_2_1, requires_cftime_1_0_2_1 = _importorskip(
6769
'cftime', minversion='1.0.2.1')
6870
has_dask, requires_dask = _importorskip('dask')

0 commit comments

Comments
 (0)