Skip to content
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

enable internal plotting with cftime datetime #2665

Merged
merged 17 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/requirements-py37-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ channels:
dependencies:
- python=3.7
- cftime
- nc-time-axis
- dask
- distributed
- h5py
Expand Down
1 change: 1 addition & 0 deletions ci/requirements-py37.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ channels:
dependencies:
- python=3.7
- cftime
- nc-time-axis
- dask
- distributed
- h5py
Expand Down
9 changes: 7 additions & 2 deletions doc/plotting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ Matplotlib syntax and function names were copied as much as possible, which
makes for an easy transition between the two.
Matplotlib must be installed before xarray can plot.

To use xarray's plotting capabilities with time coordinates containing
`cftime.datetime` objects
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
`nc-time-axis <https://github.com/SciTools/nc-time-axis>` v1.2.0 or later
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
needs to be installed.

For more extensive plotting applications consider the following projects:

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

As an alternative, also a step plot similar to matplotlib's ``plt.step`` can be
made using 1D data.
made using 1D data.

.. ipython:: python

Expand All @@ -248,7 +253,7 @@ when plotting data grouped with :py:func:`xarray.Dataset.groupby_bins`.
plt.ylim(-20,30)
@savefig plotting_example_step_groupby.png width=4in
plt.title('Zonal mean temperature')

In this case, the actual boundaries of the bins are used and the ``where`` argument
is ignored.

Expand Down
6 changes: 5 additions & 1 deletion doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ Breaking changes
- Remove support for Python 2. This is the first version of xarray that is
Python 3 only. (:issue:`1876`).
By `Joe Hamman <https://github.com/jhamman>`_.
- The `compat` argument to `Dataset` and the `encoding` argument to
- The `compat` argument to `Dataset` and the `encoding` argument to
`DataArray` are deprecated and will be removed in a future release.
(:issue:`1188`)
By `Maximilian Roos <https://github.com/max-sixty>`_.

Enhancements
~~~~~~~~~~~~

- Internal plotting now supports `cftime.datetime` objects as time series.
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
(:issue:``2164``)
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
By `Spencer Clark <https://github.com/spencerkclark>` and
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
`Julius Busecke <https://github.com/jbusecke>`_.
- Add ``data=False`` option to ``to_dict()`` methods. (:issue:`2656`)
By `Ryan Abernathey <https://github.com/rabernat>`_
- :py:meth:`~xarray.DataArray.coarsen` and
Expand Down
2 changes: 1 addition & 1 deletion xarray/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ def resample(self, indexer=None, skipna=None, closed=None, label=None,
array([ 0. , 0.032258, 0.064516, ..., 10.935484, 10.967742, 11. ])
Coordinates:
* time (time) datetime64[ns] 1999-12-15 1999-12-16 1999-12-17 ...

Limit scope of upsampling method
>>> da.resample(time='1D').nearest(tolerance='1D')
<xarray.DataArray (time: 337)>
Expand Down
11 changes: 0 additions & 11 deletions xarray/plot/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import numpy as np
import pandas as pd

from xarray.core.common import contains_cftime_datetimes

from .facetgrid import _easy_facetgrid
from .utils import (
_add_colorbar, _ensure_plottable, _infer_interval_breaks, _infer_xy_labels,
Expand Down Expand Up @@ -139,15 +137,6 @@ def plot(darray, row=None, col=None, col_wrap=None, ax=None, hue=None,
"""
darray = darray.squeeze()

if contains_cftime_datetimes(darray):
raise NotImplementedError(
'Built-in plotting of arrays of cftime.datetime objects or arrays '
'indexed by cftime.datetime objects is currently not implemented '
'within xarray. A possible workaround is to use the '
'nc-time-axis package '
'(https://github.com/SciTools/nc-time-axis) to convert the dates '
'to a plottable type and plot your data directly with matplotlib.')

plot_dims = set(darray.dims)
plot_dims.discard(row)
plot_dims.discard(col)
Expand Down
35 changes: 29 additions & 6 deletions xarray/plot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

from ..core.options import OPTIONS
from ..core.utils import is_scalar
from distutils.version import LooseVersion

try:
import nc_time_axis
if LooseVersion(nc_time_axis.__version__) < LooseVersion('1.2.0'):
nc_time_axis_available = False
else:
nc_time_axis_available = True
except ImportError:
nc_time_axis_available = False

ROBUST_PERCENTILE = 2.0

Expand Down Expand Up @@ -471,16 +481,29 @@ def _ensure_plottable(*args):
"""
numpy_types = [np.floating, np.integer, np.timedelta64, np.datetime64]
other_types = [datetime]

try:
import cftime
cftime_datetime = [cftime.datetime]
except ImportError:
cftime_datetime = []
other_types = other_types + cftime_datetime
for x in args:
if not (_valid_numpy_subdtype(np.array(x), numpy_types)
or _valid_other_type(np.array(x), other_types)):
raise TypeError('Plotting requires coordinates to be numeric '
'or dates of type np.datetime64 or '
'datetime.datetime or pd.Interval.')


def _ensure_numeric(arr):
'or dates of type np.datetime64, '
'datetime.datetime, cftime.datetime or '
'pd.Interval.')
if (_valid_other_type(np.array(x), cftime_datetime)
and not nc_time_axis_available):
raise ImportError('Plotting of arrays of cftime.datetime '
'objects or arrays indexed by '
'cftime.datetime objects requires the '
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
'optional `nc-time-axis` (v1.2.0 or later) '
'package.')


def _numeric(arr):
numpy_types = [np.floating, np.integer]
return _valid_numpy_subdtype(arr, numpy_types)

Expand Down
2 changes: 2 additions & 0 deletions xarray/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def LooseVersion(vstring):
has_pynio, requires_pynio = _importorskip('Nio')
has_pseudonetcdf, requires_pseudonetcdf = _importorskip('PseudoNetCDF')
has_cftime, requires_cftime = _importorskip('cftime')
has_nc_time_axis, requires_nc_time_axis = _importorskip('nc_time_axis',
minversion='1.2.0')
has_cftime_1_0_2_1, requires_cftime_1_0_2_1 = _importorskip(
'cftime', minversion='1.0.2.1')
has_dask, requires_dask = _importorskip('dask')
Expand Down
125 changes: 103 additions & 22 deletions xarray/tests/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

from . import (
assert_array_equal, assert_equal, raises_regex, requires_cftime,
requires_matplotlib, requires_matplotlib2, requires_seaborn)
requires_matplotlib, requires_matplotlib2, requires_seaborn,
requires_nc_time_axis)
from . import has_nc_time_axis

# import mpl and change the backend before other mpl imports
try:
Expand Down Expand Up @@ -1828,6 +1830,106 @@ def test_datetime_line_plot(self):
self.darray.plot.line()


@requires_nc_time_axis
@requires_cftime
class TestCFDatetimePlot(PlotTestCase):
@pytest.fixture(autouse=True)
def setUp(self):
'''
Create a DataArray with a time-axis that contains cftime.datetime
objects.
'''
month = np.arange(1, 13, 1)
data = np.sin(2 * np.pi * month / 12.0)
darray = DataArray(data, dims=['time'])
darray.coords['time'] = xr.cftime_range(start='2017',
periods=12,
freq='1M',
calendar='noleap')

self.darray = darray

def test_cfdatetime_line_plot(self):
# test if line plot raises no Exception
self.darray.plot.line()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think you could use the 2D DataArray for the line plot test instead of creating a separate DataArray for the 1D case (i.e. just use isel to select a single point along the 'x' dimension before calling plot).



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a pcolormesh and a contour test too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think the ones I added look ok?

spencerkclark marked this conversation as resolved.
Show resolved Hide resolved
@requires_nc_time_axis
@requires_cftime
class TestCFDatetimePcolormesh(PlotTestCase):
@pytest.fixture(autouse=True)
def setUp(self):
'''
Create a DataArray with a time-axis that contains cftime.datetime
objects.
'''
month = np.arange(1, 13, 1)
data = np.random.rand(4, 12)

darray = DataArray(data, dims=['x', 'time'])
darray.coords['time'] = xr.cftime_range(start='2017',
periods=12,
freq='1M',
calendar='noleap')

self.darray = darray

def test_cfdatetime_line_plot(self):
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
# test if line plot raises no Exception
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
self.darray.plot.pcolormesh()


@requires_nc_time_axis
@requires_cftime
class TestCFDateContour(PlotTestCase):
@pytest.fixture(autouse=True)
def setUp(self):
'''
Create a DataArray with a time-axis that contains cftime.datetime
objects.
'''
month = np.arange(1, 13, 1)
data = np.random.rand(4, 12)

darray = DataArray(data, dims=['x', 'time'])
darray.coords['time'] = xr.cftime_range(start='2017',
periods=12,
freq='1M',
calendar='noleap')

self.darray = darray

def test_cfdatetime_line_plot(self):
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
# test if line plot raises no Exception
self.darray.plot.contour()


@requires_cftime
@pytest.mark.skipif(has_nc_time_axis, reason='nc_time_axis is installed')
class TestNcAxisNotInstalled(PlotTestCase):
@pytest.fixture(autouse=True)
def setUp(self):
'''
Create a DataArray with a time-axis that contains cftime.datetime
objects.
'''
month = np.arange(1, 13, 1)
data = np.sin(2 * np.pi * month / 12.0)
darray = DataArray(data, dims=['time'])
darray.coords['time'] = xr.cftime_range(start='2017',
periods=12,
freq='1M',
calendar='noleap')

self.darray = darray

def test_ncaxis_notinstalled_line_plot(self):
# test if line plot raises no Exception
jbusecke marked this conversation as resolved.
Show resolved Hide resolved
with raises_regex(ImportError,
'optional `nc-time-axis`'):
self.darray.plot.line()


@requires_seaborn
def test_import_seaborn_no_warning():
# GH1633
Expand All @@ -1844,27 +1946,6 @@ def test_plot_seaborn_no_import_warning():
assert len(record) == 0


@requires_cftime
def test_plot_cftime_coordinate_error():
cftime = _import_cftime()
time = cftime.num2date(np.arange(5), units='days since 0001-01-01',
calendar='noleap')
data = DataArray(np.arange(5), coords=[time], dims=['time'])
with raises_regex(TypeError,
'requires coordinates to be numeric or dates'):
data.plot()


@requires_cftime
def test_plot_cftime_data_error():
cftime = _import_cftime()
data = cftime.num2date(np.arange(5), units='days since 0001-01-01',
calendar='noleap')
data = DataArray(data, coords=[np.arange(5)], dims=['x'])
with raises_regex(NotImplementedError, 'cftime.datetime'):
data.plot()


test_da_list = [DataArray(easy_array((10, ))),
DataArray(easy_array((10, 3))),
DataArray(easy_array((10, 3, 2)))]
Expand Down
1 change: 1 addition & 0 deletions xarray/util/print_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def show_versions(as_json=False):
("Nio", lambda mod: mod.__version__),
("zarr", lambda mod: mod.__version__),
("cftime", lambda mod: mod.__version__),
("nc_time_axis", lambda mod: mod.__version__),
("PseudonetCDF", lambda mod: mod.__version__),
("rasterio", lambda mod: mod.__version__),
("cfgrib", lambda mod: mod.__version__),
Expand Down