diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 459826da9fd..aae10244a9c 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -62,6 +62,10 @@ Bug fixes DataArray constructor (:issue:`1709`). By `Stephan Hoyer `_. +- Raise ``NotImplementedError`` when attempting to save a MultiIndex to a + netCDF file (:issue:`1547`). + By `Stephan Hoyer `_. + Testing ~~~~~~~ diff --git a/xarray/conventions.py b/xarray/conventions.py index 87fb6694d4e..bb515e7dff0 100644 --- a/xarray/conventions.py +++ b/xarray/conventions.py @@ -17,7 +17,7 @@ from .core import duck_array_ops, indexing, ops, utils from .core.formatting import format_timestamp, first_n_items, last_item -from .core.variable import as_variable, Variable +from .core.variable import as_variable, IndexVariable, Variable from .core.pycompat import iteritems, OrderedDict, PY3, basestring @@ -832,6 +832,15 @@ def _infer_dtype(array, name=None): def ensure_dtype_not_object(var, name=None): # TODO: move this from conventions to backends? (it's not CF related) if var.dtype.kind == 'O': + if (isinstance(var, IndexVariable) and + isinstance(var.to_index(), pd.MultiIndex)): + raise NotImplementedError( + 'variable {!r} is a MultiIndex, which cannot yet be ' + 'serialized to netCDF files ' + '(https://github.com/pydata/xarray/issues/1077). Use ' + 'reset_index() to convert MultiIndex levels into coordinate ' + 'variables instead.'.format(name)) + dims, data, attrs, encoding = _var_as_tuple(var) missing = pd.isnull(data) if missing.any(): diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 316c97eb487..9af12df7d5e 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -719,6 +719,13 @@ def test_append_overwrite_values(self): def test_vectorized_indexing(self): self._test_vectorized_indexing(vindex_support=False) + def test_multiindex_not_implemented(self): + ds = (Dataset(coords={'y': ('x', [1, 2]), 'z': ('x', ['a', 'b'])}) + .set_index(x=['y', 'z'])) + with raises_regex(NotImplementedError, 'MultiIndex'): + with self.roundtrip(ds): + pass + _counter = itertools.count()