From 3e3f5ec00916b6d98763b937cff4478f221dbc39 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Thu, 14 Nov 2019 15:09:28 +0100 Subject: [PATCH 1/4] ensure rename does not change index type --- doc/whats-new.rst | 4 ++++ xarray/core/dataset.py | 3 ++- xarray/tests/test_dataset.py | 25 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index abd94779435..988509bdb06 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -79,6 +79,10 @@ New Features Bug fixes ~~~~~~~~~ +- Ensure an index of type ``CFTimeIndex`` is not converted to a ``DatetimeIndex`` when + calling :py:meth:`Dataset.rename` (also :py:meth:`Dataset.rename_dims` + and :py:meth:`xr.Dataset.rename_vars`). By `Mathias Hauser `_ + (:issue:`3522`). - Fix a bug in `set_index` in case that an existing dimension becomes a level variable of MultiIndex. (:pull:`3520`) By `Keisuke Fujii `_. - Harmonize `_FillValue`, `missing_value` during encoding and decoding steps. (:pull:`3502`) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index de713b830f2..ae3c866d052 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -2667,7 +2667,8 @@ def _rename_indexes(self, name_dict, dims_set): verify_integrity=False, ) else: - index = pd.Index(v, name=new_name) + index = v + index.name = new_name indexes[new_name] = index return indexes diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 67d3b3198dc..f678e9dbf4e 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -8,6 +8,7 @@ import numpy as np import pandas as pd import pytest +from pandas.core.indexes.datetimes import DatetimeIndex import xarray as xr from xarray import ( @@ -22,6 +23,7 @@ open_dataset, set_options, ) +from xarray.coding.cftimeindex import CFTimeIndex from xarray.core import dtypes, indexing, utils from xarray.core.common import duck_array_ops, full_like from xarray.core.npcompat import IS_NEP18_ACTIVE @@ -2458,6 +2460,29 @@ def test_rename_vars(self): with pytest.raises(ValueError): original.rename_vars(names_dict_bad) + def test_rename_does_not_change_index_type(self): + # make sure CFTimeIndex is not converted to DatetimeIndex #3522 + + time = xr.cftime_range(start="2000", periods=6, freq="2MS", calendar="noleap") + + ds = Dataset(coords={"time": time}) + + # make sure renaming does not change the type of the index + assert isinstance(ds.indexes["time"], CFTimeIndex) + assert isinstance(ds.rename().indexes["time"], CFTimeIndex) + assert isinstance(ds.rename_dims().indexes["time"], CFTimeIndex) + assert isinstance(ds.rename_vars().indexes["time"], CFTimeIndex) + + time = pd.date_range(start="2000", periods=6, freq="2MS") + + ds = Dataset(coords={"time": time}) + + # make sure renaming does not change the type of the index + assert isinstance(ds.indexes["time"], DatetimeIndex) + assert isinstance(ds.rename().indexes["time"], DatetimeIndex) + assert isinstance(ds.rename_dims().indexes["time"], DatetimeIndex) + assert isinstance(ds.rename_vars().indexes["time"], DatetimeIndex) + def test_swap_dims(self): original = Dataset({"x": [1, 2, 3], "y": ("x", list("abc")), "z": 42}) expected = Dataset({"z": 42}, {"x": ("y", [1, 2, 3]), "y": list("abc")}) From 5064cd2f101843d7fbba274b054c87c8df5b3713 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Thu, 14 Nov 2019 15:20:00 +0100 Subject: [PATCH 2/4] test requires cftime --- xarray/tests/test_dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index f678e9dbf4e..eca80699610 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -2460,6 +2460,7 @@ def test_rename_vars(self): with pytest.raises(ValueError): original.rename_vars(names_dict_bad) + @requires_cftime def test_rename_does_not_change_index_type(self): # make sure CFTimeIndex is not converted to DatetimeIndex #3522 From 3b854952d113dd3df82f19b2962e01db21224c56 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Fri, 15 Nov 2019 15:06:28 +0100 Subject: [PATCH 3/4] test orig.indexes[time].name is conserved --- xarray/tests/test_dataset.py | 49 ++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index eca80699610..780843f2e61 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -2461,28 +2461,51 @@ def test_rename_vars(self): original.rename_vars(names_dict_bad) @requires_cftime - def test_rename_does_not_change_index_type(self): + def test_rename_does_not_change_CFTimeIndex_type(self): # make sure CFTimeIndex is not converted to DatetimeIndex #3522 time = xr.cftime_range(start="2000", periods=6, freq="2MS", calendar="noleap") + orig = Dataset(coords={"time": time}) - ds = Dataset(coords={"time": time}) + renamed = orig.rename(time="time_new") + assert "time_new" in renamed.indexes + assert isinstance(renamed.indexes["time_new"], CFTimeIndex) + assert renamed.indexes["time_new"].name == "time_new" - # make sure renaming does not change the type of the index - assert isinstance(ds.indexes["time"], CFTimeIndex) - assert isinstance(ds.rename().indexes["time"], CFTimeIndex) - assert isinstance(ds.rename_dims().indexes["time"], CFTimeIndex) - assert isinstance(ds.rename_vars().indexes["time"], CFTimeIndex) + # check original has not changed + assert "time" in orig.indexes + assert isinstance(orig.indexes["time"], CFTimeIndex) + assert orig.indexes["time"].name == "time" + + # note: rename_dims(time="time_new") drops "ds.indexes" + renamed = orig.rename_dims() + assert isinstance(renamed.indexes["time"], CFTimeIndex) + + renamed = orig.rename_vars() + assert isinstance(renamed.indexes["time"], CFTimeIndex) + + def test_rename_does_not_change_DatetimeIndex_type(self): + # make sure DatetimeIndex is conderved on rename time = pd.date_range(start="2000", periods=6, freq="2MS") + orig = Dataset(coords={"time": time}) + + renamed = orig.rename(time="time_new") + assert "time_new" in renamed.indexes + assert isinstance(renamed.indexes["time_new"], DatetimeIndex) + assert renamed.indexes["time_new"].name == "time_new" + + # check original has not changed + assert "time" in orig.indexes + assert isinstance(orig.indexes["time"], DatetimeIndex) + assert orig.indexes["time"].name == "time" - ds = Dataset(coords={"time": time}) + # note: rename_dims(time="time_new") drops "ds.indexes" + renamed = orig.rename_dims() + assert isinstance(renamed.indexes["time"], DatetimeIndex) - # make sure renaming does not change the type of the index - assert isinstance(ds.indexes["time"], DatetimeIndex) - assert isinstance(ds.rename().indexes["time"], DatetimeIndex) - assert isinstance(ds.rename_dims().indexes["time"], DatetimeIndex) - assert isinstance(ds.rename_vars().indexes["time"], DatetimeIndex) + renamed = orig.rename_vars() + assert isinstance(renamed.indexes["time"], DatetimeIndex) def test_swap_dims(self): original = Dataset({"x": [1, 2, 3], "y": ("x", list("abc")), "z": 42}) From ec7615404a54ce9199780ef371b39c7f01fb0567 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Fri, 15 Nov 2019 15:07:27 +0100 Subject: [PATCH 4/4] use index.rename() --- xarray/core/dataset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index ae3c866d052..793a55a87c2 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -2667,8 +2667,7 @@ def _rename_indexes(self, name_dict, dims_set): verify_integrity=False, ) else: - index = v - index.name = new_name + index = v.rename(new_name) indexes[new_name] = index return indexes