diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 27709a09e7a..d07872a9364 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -32,6 +32,8 @@ Bug fixes By `Mayeul d'Avezac `_. - Return correct count for scalar datetime64 arrays (:issue:`2770`) By `Dan Nowacki `_. +- A deep copy deep-copies the coords (:issue:`1463`) + By `Martin Pletcher `_. .. _whats-new.0.12.1: diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 96c6b7bd59b..41f8795b595 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -1906,7 +1906,8 @@ def copy(self, deep=True, data=None): Parameters ---------- deep : bool, optional - Deep is always ignored. + Deep is ignored when data is given. Whether the data array is + loaded into memory and copied onto the new object. Default is True. data : array_like, optional Data to use in the new object. Must have same shape as original. @@ -1917,7 +1918,14 @@ def copy(self, deep=True, data=None): data copied from original. """ if data is None: - data = self._data + if deep: + # self._data should be a `PandasIndexAdapter` instance at this + # point, which doesn't have a copy method, so make a deep copy + # of the underlying `pandas.MultiIndex` and create a new + # `PandasIndexAdapter` instance with it. + data = PandasIndexAdapter(self._data.array.copy(deep=True)) + else: + data = self._data else: data = as_compatible_data(data) if self.shape != data.shape: diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 58eb6a1d6dc..6084368d8a1 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -3297,6 +3297,28 @@ def test_copy_with_data(self): expected.data = new_data assert_identical(expected, actual) + @pytest.mark.parametrize('deep, expected_orig', [ + [True, + xr.DataArray(xr.IndexVariable('a', np.array([1, 2])), + coords={'a': [1, 2]}, dims=['a'])], + [False, + xr.DataArray(xr.IndexVariable('a', np.array([999, 2])), + coords={'a': [999, 2]}, dims=['a'])]]) + def test_copy_coords(self, deep, expected_orig): + da = xr.DataArray( + np.ones([2, 2, 2]), + coords={'a': [1, 2], 'b': ['x', 'y'], 'c': [0, 1]}, + dims=['a', 'b', 'c']) + da_cp = da.copy(deep) + da_cp['a'].data[0] = 999 + + expected_cp = xr.DataArray( + xr.IndexVariable('a', np.array([999, 2])), + coords={'a': [999, 2]}, dims=['a']) + assert_identical(da_cp['a'], expected_cp) + + assert_identical(da['a'], expected_orig) + def test_real_and_imag(self): array = DataArray(1 + 2j) assert_identical(array.real, DataArray(1)) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 3ace80f5eea..b98c0509754 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -1923,6 +1923,29 @@ def test_copy_with_data(self): expected[k].data = v assert_identical(expected, actual) + @pytest.mark.parametrize('deep, expected_orig', [ + [True, + xr.DataArray(xr.IndexVariable('a', np.array([1, 2])), + coords={'a': [1, 2]}, dims=['a'])], + [False, + xr.DataArray(xr.IndexVariable('a', np.array([999, 2])), + coords={'a': [999, 2]}, dims=['a'])]]) + def test_copy_coords(self, deep, expected_orig): + ds = xr.DataArray( + np.ones([2, 2, 2]), + coords={'a': [1, 2], 'b': ['x', 'y'], 'c': [0, 1]}, + dims=['a', 'b', 'c'], + name='value').to_dataset() + ds_cp = ds.copy(deep=deep) + ds_cp.coords['a'].data[0] = 999 + + expected_cp = xr.DataArray( + xr.IndexVariable('a', np.array([999, 2])), + coords={'a': [999, 2]}, dims=['a']) + assert_identical(ds_cp.coords['a'], expected_cp) + + assert_identical(ds.coords['a'], expected_orig) + def test_copy_with_data_errors(self): orig = create_test_data() new_var1 = np.arange(orig['var1'].size).reshape(orig['var1'].shape)