diff --git a/doc/whats-new.rst b/doc/whats-new.rst index f41a9d78048..cb7bcf4fb34 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -30,6 +30,9 @@ Enhancements Bug fixes ~~~~~~~~~ +- Improved error handling and documentation for `.expand_dims()` + read-only view. + .. _whats-new.0.12.3: v0.12.3 (10 July 2019) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index b93e4a5cb3c..c82ca63414c 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -1271,7 +1271,9 @@ def expand_dims(self, dim: Union[None, Hashable, Sequence[Hashable], Mapping[Hashable, Any]] = None, axis=None, **dim_kwargs: Any) -> 'DataArray': """Return a new object with an additional axis (or axes) inserted at - the corresponding position in the array shape. + the corresponding position in the array shape. The new object is a + view into the underlying array, not a copy. + If dim is already a scalar coordinate, it will be promoted to a 1D coordinate consisting of a single value. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index ebc23773738..c67583267e5 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -2505,7 +2505,8 @@ def swap_dims(self, dims_dict, inplace=None): def expand_dims(self, dim=None, axis=None, **dim_kwargs): """Return a new object with an additional axis (or axes) inserted at - the corresponding position in the array shape. + the corresponding position in the array shape. The new object is a + view into the underlying array, not a copy. If dim is already a scalar coordinate, it will be promoted to a 1D coordinate consisting of a single value. diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index 02953b74fa4..e262d9ee24b 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -1177,7 +1177,15 @@ def __getitem__(self, key): def __setitem__(self, key, value): array, key = self._indexing_array_and_key(key) - array[key] = value + try: + array[key] = value + except ValueError: + # More informative exception if read-only view + if not array.flags.writeable and not array.flags.owndata: + raise ValueError("Assignment destination is a view. " + "Do you want to .copy() array first?") + else: + raise class DaskIndexingAdapter(ExplicitlyIndexedNDArrayMixin): diff --git a/xarray/tests/test_indexing.py b/xarray/tests/test_indexing.py index c044d2ed1f3..64eee80d4eb 100644 --- a/xarray/tests/test_indexing.py +++ b/xarray/tests/test_indexing.py @@ -144,6 +144,16 @@ def test_indexer(data, x, expected_pos, expected_idx=None): [True, True, True, True, False, False, False, False], pd.MultiIndex.from_product([[1, 2], [-1, -2]])) + def test_read_only_view(self): + from collections import OrderedDict + arr = DataArray(np.random.rand(3, 3), + coords={'x': np.arange(3), 'y': np.arange(3)}, + dims=('x', 'y')) # Create a 2D DataArray + arr = arr.expand_dims(OrderedDict([('z', 3)]), -1) # New dimension 'z' + arr['z'] = np.arange(3) # New coords to dimension 'z' + with pytest.raises(ValueError, match='Do you want to .copy()'): + arr.loc[0, 0, 0] = 999 + class TestLazyArray: def test_slice_slice(self):