From 9eafe360fc13691f2bf9b317763815d9540ca0ec Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Wed, 29 Nov 2017 09:24:55 +0900 Subject: [PATCH 01/13] Fix in NumpyVindexAdapter.__setitem__ and DataArray.__setitem__ --- xarray/core/dataarray.py | 4 +++- xarray/core/nputils.py | 8 +++++--- xarray/tests/test_dataarray.py | 30 ++++++++++++++++++++++++++++++ xarray/tests/test_variable.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 0516b473994..ad6a7d52126 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -484,7 +484,9 @@ def __setitem__(self, key, value): if isinstance(key, basestring): self.coords[key] = value else: - # xarray-style array indexing + # DataArray key -> Variable key + key = {k: v.variable if isinstance(v, DataArray) else v + for k, v in self._item_key_to_dict(key).items()} self.variable[key] = value def __delitem__(self, key): diff --git a/xarray/core/nputils.py b/xarray/core/nputils.py index a721425b839..bba431e3caf 100644 --- a/xarray/core/nputils.py +++ b/xarray/core/nputils.py @@ -129,6 +129,8 @@ def __getitem__(self, key): def __setitem__(self, key, value): """Value must have dimensionality matching the key.""" - mixed_positions, vindex_positions = _advanced_indexer_subspaces(key) - self._array[key] = np.moveaxis(value, vindex_positions, - mixed_positions) + if np.isscalar(value): + self._array[key] = value + else: + mixed_pos, vindex_pos = _advanced_indexer_subspaces(key) + self._array[key] = np.moveaxis(value, vindex_pos, mixed_pos) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index c86f706f2ce..cdd34852058 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -526,6 +526,36 @@ def test_setitem(self): expected[t] = 1 self.assertArrayEqual(orig.values, expected) + def test_setitem_fancy(self): + # vectorized indexing + da = DataArray(np.ones((3, 2)), dims=['x', 'y']) + ind = Variable(['a'], [0, 1]) + da[dict(x=ind, y=ind)] = 0 + expected = DataArray([[0, 1], [1, 0], [1, 1]], dims=['x', 'y']) + self.assertDataArrayIdentical(expected, da) + # assign another 0d-variable + da[dict(x=ind, y=ind)] = Variable((), 0) + expected = DataArray([[0, 1], [1, 0], [1, 1]], dims=['x', 'y']) + self.assertDataArrayIdentical(expected, da) + # assign another 1d-variable + da[dict(x=ind, y=ind)] = Variable(['a'], [2, 3]) + expected = DataArray([[2, 1], [1, 3], [1, 1]], dims=['x', 'y']) + self.assertVariableIdentical(expected, da) + + # 2d-vectorized indexing + da = DataArray(np.ones((3, 2)), dims=['x', 'y']) + ind_x = DataArray([[0, 1]], dims=['a', 'b']) + ind_y = DataArray([[1, 0]], dims=['a', 'b']) + da[dict(x=ind_x, y=ind_y)] = 0 + expected = DataArray([[1, 0], [0, 1], [1, 1]], dims=['x', 'y']) + self.assertVariableIdentical(expected, da) + + da = DataArray(np.ones((3, 2)), dims=['x', 'y']) + ind = Variable(['a'], [0, 1]) + da[ind] = 0 + expected = DataArray([[0, 0], [0, 0], [1, 1]], dims=['x', 'y']) + self.assertVariableIdentical(expected, da) + def test_contains(self): data_array = DataArray(1, coords={'x': 2}) with pytest.warns(FutureWarning): diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py index bdaee5edb7a..02ae553e0df 100644 --- a/xarray/tests/test_variable.py +++ b/xarray/tests/test_variable.py @@ -1440,6 +1440,39 @@ def test_setitem(self): v[dict(x=[True, False], y=[False, True, False])] = 1 self.assertTrue(v[0, 1] == 1) + def test_setitem_fancy(self): + # vectorized indexing + v = Variable(['x', 'y'], np.ones((3, 2))) + ind = Variable(['a'], [0, 1]) + v[dict(x=ind, y=ind)] = 0 + expected = Variable(['x', 'y'], [[0, 1], [1, 0], [1, 1]]) + self.assertVariableIdentical(expected, v) + # assign another 0d-variable + v[dict(x=ind, y=ind)] = Variable((), 0) + expected = Variable(['x', 'y'], [[0, 1], [1, 0], [1, 1]]) + self.assertVariableIdentical(expected, v) + # assign another 1d-variable + v[dict(x=ind, y=ind)] = Variable(['a'], [2, 3]) + expected = Variable(['x', 'y'], [[2, 1], [1, 3], [1, 1]]) + self.assertVariableIdentical(expected, v) + + # 2d-vectorized indexing + v = Variable(['x', 'y'], np.ones((3, 2))) + ind_x = Variable(['a', 'b'], [[0, 1]]) + ind_y = Variable(['a', 'b'], [[1, 0]]) + v[dict(x=ind_x, y=ind_y)] = 0 + expected = Variable(['x', 'y'], [[1, 0], [0, 1], [1, 1]]) + self.assertVariableIdentical(expected, v) + + # vindex with slice + v = Variable(['x', 'y', 'z'], np.ones((4, 3, 2))) + ind = Variable(['a'], [0, 1]) + v[dict(y=ind, z=ind)] = 0 + expected = Variable(['x', 'y', 'z'], np.ones((4, 3, 2))) + expected[:, 0, 0] = 0 + expected[:, 1, 1] = 0 + self.assertVariableIdentical(expected, v) + # dimension broadcast v = Variable(['x', 'y'], np.ones((3, 2))) ind = Variable(['a', 'b'], [[0, 1]]) From 2aff4ced606aecb6f76093eb248c689ecf64866c Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Wed, 29 Nov 2017 09:32:33 +0900 Subject: [PATCH 02/13] Update what's new --- doc/whats-new.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 472cd47adf1..0e0b6249ec1 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -24,6 +24,10 @@ Enhancements Bug fixes ~~~~~~~~~ +- Bug fix in vectorized assignment (:issue:`1743`, `1744`). + By `Keisuke Fujii `_. + + .. _whats-new.0.10.0: v0.10.0 (20 November 2017) From 22c12957fa04623e680ead4f0f195ec599b89351 Mon Sep 17 00:00:00 2001 From: fujiisoup Date: Wed, 29 Nov 2017 23:02:57 +0900 Subject: [PATCH 03/13] Broadcasting in setitem --- xarray/core/nputils.py | 8 ++--- xarray/core/variable.py | 12 +++++-- xarray/tests/test_nputils.py | 12 +++++++ xarray/tests/test_variable.py | 63 ++++++++++++++++++++++------------- 4 files changed, 64 insertions(+), 31 deletions(-) diff --git a/xarray/core/nputils.py b/xarray/core/nputils.py index bba431e3caf..a721425b839 100644 --- a/xarray/core/nputils.py +++ b/xarray/core/nputils.py @@ -129,8 +129,6 @@ def __getitem__(self, key): def __setitem__(self, key, value): """Value must have dimensionality matching the key.""" - if np.isscalar(value): - self._array[key] = value - else: - mixed_pos, vindex_pos = _advanced_indexer_subspaces(key) - self._array[key] = np.moveaxis(value, vindex_pos, mixed_pos) + mixed_positions, vindex_positions = _advanced_indexer_subspaces(key) + self._array[key] = np.moveaxis(value, vindex_positions, + mixed_positions) diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 53b4bf60c5c..35b45e4ae9a 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -639,15 +639,21 @@ def __setitem__(self, key, value): if isinstance(value, Variable): value = value.set_dims(dims).data - - if new_order: - value = duck_array_ops.asarray(value) + else: # first broadcast value + value = as_compatible_data(value) if value.ndim > len(dims): raise ValueError( 'shape mismatch: value array of shape %s could not be' 'broadcast to indexing result with %s dimensions' % (value.shape, len(dims))) + if value.ndim == 0: + value = Variable((), value).set_dims(dims).data + else: + value = Variable(dims[-value.ndim:], value).set_dims(dims).data + + if new_order: + value = duck_array_ops.asarray(value) value = value[(len(dims) - value.ndim) * (np.newaxis,) + (Ellipsis,)] value = np.moveaxis(value, new_order, range(len(new_order))) diff --git a/xarray/tests/test_nputils.py b/xarray/tests/test_nputils.py index 83445e4639f..18f95e05e24 100644 --- a/xarray/tests/test_nputils.py +++ b/xarray/tests/test_nputils.py @@ -28,3 +28,15 @@ def test_vindex(): vindex[[0, 1], [0, 1], :] = vindex[[0, 1], [0, 1], :] vindex[[0, 1], :, [0, 1]] = vindex[[0, 1], :, [0, 1]] vindex[:, [0, 1], [0, 1]] = vindex[:, [0, 1], [0, 1]] + +def test_vindex_4d(): + x = np.arange(3 * 4 * 5 * 6).reshape((3, 4, 5, 6)) + vindex = NumpyVIndexAdapter(x) + + # getitem + assert_array_equal(vindex[0], x[0]) + assert_array_equal(vindex[[1, 2], [1, 2]], x[[1, 2], [1, 2]]) + assert vindex[[0, 1], [0, 1], :].shape == (2, 5, 6) + assert vindex[[0, 1], :, [0, 1]].shape == (2, 4, 6) + assert vindex[:, [0, 1], [0, 1]].shape == (2, 3, 6) + assert vindex[:, [0, 1], :, [0, 1]].shape == (2, 3, 5) diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py index 02ae553e0df..2e5446bcafc 100644 --- a/xarray/tests/test_variable.py +++ b/xarray/tests/test_variable.py @@ -1441,36 +1441,53 @@ def test_setitem(self): self.assertTrue(v[0, 1] == 1) def test_setitem_fancy(self): - # vectorized indexing - v = Variable(['x', 'y'], np.ones((3, 2))) - ind = Variable(['a'], [0, 1]) - v[dict(x=ind, y=ind)] = 0 - expected = Variable(['x', 'y'], [[0, 1], [1, 0], [1, 1]]) - self.assertVariableIdentical(expected, v) - # assign another 0d-variable - v[dict(x=ind, y=ind)] = Variable((), 0) - expected = Variable(['x', 'y'], [[0, 1], [1, 0], [1, 1]]) - self.assertVariableIdentical(expected, v) - # assign another 1d-variable - v[dict(x=ind, y=ind)] = Variable(['a'], [2, 3]) - expected = Variable(['x', 'y'], [[2, 1], [1, 3], [1, 1]]) - self.assertVariableIdentical(expected, v) + # assignment which should work as np.ndarray does + def assert_assigned_2d(array, key_x, key_y, values): + expected = array.copy() + expected[key_x, key_y] = values + v = Variable(['x', 'y'], array) + v[dict(x=key_x, y=key_y)] = values + self.assertArrayEqual(expected, v) + + # 1d vectorized indexing + assert_assigned_2d(np.random.randn(4, 3), + key_x=Variable(['a'], [0, 1]), + key_y=Variable(['a'], [0, 1]), + values=0) + assert_assigned_2d(np.random.randn(4, 3), + key_x=Variable(['a'], [0, 1]), + key_y=Variable(['a'], [0, 1]), + values=Variable((), 0)) + assert_assigned_2d(np.random.randn(4, 3), + key_x=Variable(['a'], [0, 1]), + key_y=Variable(['a'], [0, 1]), + values=Variable(('a'), [3, 2])) + assert_assigned_2d(np.random.randn(4, 3), + key_x=slice(None), + key_y=Variable(['a'], [0, 1]), + values=Variable(('a'), [3, 2])) # 2d-vectorized indexing - v = Variable(['x', 'y'], np.ones((3, 2))) - ind_x = Variable(['a', 'b'], [[0, 1]]) - ind_y = Variable(['a', 'b'], [[1, 0]]) - v[dict(x=ind_x, y=ind_y)] = 0 - expected = Variable(['x', 'y'], [[1, 0], [0, 1], [1, 1]]) - self.assertVariableIdentical(expected, v) + assert_assigned_2d(np.random.randn(4, 3), + key_x=Variable(['a', 'b'], [[0, 1]]), + key_y=Variable(['a', 'b'], [[1, 0]]), + values=0) + assert_assigned_2d(np.random.randn(4, 3), + key_x=Variable(['a', 'b'], [[0, 1]]), + key_y=Variable(['a', 'b'], [[1, 0]]), + values=[0]) + assert_assigned_2d(np.random.randn(5, 4), + key_x=Variable(['a', 'b'], [[0, 1], [2, 3]]), + key_y=Variable(['a', 'b'], [[1, 0], [3, 3]]), + values=[2, 3]) # vindex with slice v = Variable(['x', 'y', 'z'], np.ones((4, 3, 2))) ind = Variable(['a'], [0, 1]) - v[dict(y=ind, z=ind)] = 0 + v[dict(x=ind, z=ind)] = 0 expected = Variable(['x', 'y', 'z'], np.ones((4, 3, 2))) - expected[:, 0, 0] = 0 - expected[:, 1, 1] = 0 + expected[0, :, 0] = 0 + expected[1, :, 1] = 0 self.assertVariableIdentical(expected, v) # dimension broadcast From dc135cdc7391d63bc4fd1f40d7b59ea9edfff571 Mon Sep 17 00:00:00 2001 From: fujiisoup Date: Wed, 29 Nov 2017 23:08:19 +0900 Subject: [PATCH 04/13] Small clean up. Revert unintended change. --- xarray/core/variable.py | 11 +++++------ xarray/tests/test_nputils.py | 12 ------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 35b45e4ae9a..5c1a2bbe1a7 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -637,20 +637,19 @@ def __setitem__(self, key, value): """ dims, index_tuple, new_order = self._broadcast_indexes(key) - if isinstance(value, Variable): - value = value.set_dims(dims).data - else: # first broadcast value + if not isinstance(value, Variable): value = as_compatible_data(value) if value.ndim > len(dims): raise ValueError( 'shape mismatch: value array of shape %s could not be' 'broadcast to indexing result with %s dimensions' % (value.shape, len(dims))) - if value.ndim == 0: - value = Variable((), value).set_dims(dims).data + value = Variable((), value) else: - value = Variable(dims[-value.ndim:], value).set_dims(dims).data + value = Variable(dims[-value.ndim:], value) + # broadcast to become assignable + value = value.set_dims(dims).data if new_order: value = duck_array_ops.asarray(value) diff --git a/xarray/tests/test_nputils.py b/xarray/tests/test_nputils.py index 18f95e05e24..83445e4639f 100644 --- a/xarray/tests/test_nputils.py +++ b/xarray/tests/test_nputils.py @@ -28,15 +28,3 @@ def test_vindex(): vindex[[0, 1], [0, 1], :] = vindex[[0, 1], [0, 1], :] vindex[[0, 1], :, [0, 1]] = vindex[[0, 1], :, [0, 1]] vindex[:, [0, 1], [0, 1]] = vindex[:, [0, 1], [0, 1]] - -def test_vindex_4d(): - x = np.arange(3 * 4 * 5 * 6).reshape((3, 4, 5, 6)) - vindex = NumpyVIndexAdapter(x) - - # getitem - assert_array_equal(vindex[0], x[0]) - assert_array_equal(vindex[[1, 2], [1, 2]], x[[1, 2], [1, 2]]) - assert vindex[[0, 1], [0, 1], :].shape == (2, 5, 6) - assert vindex[[0, 1], :, [0, 1]].shape == (2, 4, 6) - assert vindex[:, [0, 1], [0, 1]].shape == (2, 3, 6) - assert vindex[:, [0, 1], :, [0, 1]].shape == (2, 3, 5) From c74c82896074faebe721e9e6df2c543a93e81fd7 Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Wed, 6 Dec 2017 15:44:35 +0900 Subject: [PATCH 05/13] Check coordinate consistency for DataArray.__setitem__ --- xarray/core/coordinates.py | 18 +++++++++++ xarray/core/dataarray.py | 6 +++- xarray/core/dataset.py | 13 ++------ xarray/tests/test_dataarray.py | 55 ++++++++++++++++++++++++++++++++++ xarray/tests/test_dataset.py | 15 ++++------ 5 files changed, 87 insertions(+), 20 deletions(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index 473611806b5..ae9eef18249 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -301,3 +301,21 @@ def __getitem__(self, key): def __unicode__(self): return formatting.indexes_repr(self) + + +def assert_coordinate_consistent(obj, coords): + """ Maeke sure the dimension coordinate of obj is + consistent with coords. + + obj: DataArray or Dataset + coords: Dict-like of variables + """ + for k in obj.dims: + # make sure there are no conflict in dimension coordinates + if (k in coords and k in obj.coords): + coord = getattr(coords[k], 'variable', coords[k]) # Variable + if not coord.equals(obj[k].variable): + raise IndexError( + 'dimension coordinate {!r} conflicts between ' + 'indexed and indexing objects:\n{}\nvs.\n{}' + .format(k, obj[k], coords[k])) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index ad6a7d52126..7b82ce8d622 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -20,7 +20,7 @@ from .alignment import align, reindex_like_indexers from .common import AbstractArray, BaseDataObject from .coordinates import (DataArrayCoordinates, LevelCoordinatesSource, - Indexes) + Indexes, assert_coordinate_consistent) from .dataset import Dataset, merge_indexes, split_indexes from .pycompat import iteritems, basestring, OrderedDict, zip, range from .variable import (as_variable, Variable, as_compatible_data, @@ -484,6 +484,10 @@ def __setitem__(self, key, value): if isinstance(key, basestring): self.coords[key] = value else: + # Coordinates in key, value and self[key] should be consistent. + obj = self[key] + if isinstance(value, DataArray): + assert_coordinate_consistent(value, obj.coords) # DataArray key -> Variable key key = {k: v.variable if isinstance(v, DataArray) else v for k, v in self._item_key_to_dict(key).items()} diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 9c99213dc23..7867484f748 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -23,7 +23,8 @@ from . import duck_array_ops from .. import conventions from .alignment import align -from .coordinates import DatasetCoordinates, LevelCoordinatesSource, Indexes +from .coordinates import (DatasetCoordinates, LevelCoordinatesSource, Indexes, + assert_coordinate_consistent) from .common import ImplementsDatasetReduce, BaseDataObject from .dtypes import is_datetime_like from .merge import (dataset_update_method, dataset_merge_method, @@ -1305,15 +1306,7 @@ def _get_indexers_coordinates(self, indexers): # we don't need to call align() explicitly, because merge_variables # already checks for exact alignment between dimension coordinates coords = merge_variables(coord_list) - - for k in self.dims: - # make sure there are not conflict in dimension coordinates - if (k in coords and k in self._variables and - not coords[k].equals(self._variables[k])): - raise IndexError( - 'dimension coordinate {!r} conflicts between ' - 'indexed and indexing objects:\n{}\nvs.\n{}' - .format(k, self._variables[k], coords[k])) + assert_coordinate_consistent(self, coords) attached_coords = OrderedDict() for k, v in coords.items(): # silently drop the conflicted variables. diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index cdd34852058..89a1038b24b 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -556,6 +556,61 @@ def test_setitem_fancy(self): expected = DataArray([[0, 0], [0, 0], [1, 1]], dims=['x', 'y']) self.assertVariableIdentical(expected, da) + def test_setitem_dataarray(self): + def get_data(): + return DataArray(np.ones((4, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': np.arange(4), 'y': ['a', 'b', 'c'], + 'non-dim': ('x', [1, 3, 4, 2])}) + + da = get_data() + # indexer with inconsistent coordinates. + ind = DataArray(np.arange(1, 4), dims=['x'], + coords={'x': np.random.randn(3)}) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da[dict(x=ind)] = 0 + + # indexer with consistent coordinates. + ind = DataArray(np.arange(1, 4), dims=['x'], + coords={'x': np.arange(1, 4)}) + da[dict(x=ind)] = 0 # should not raise + assert np.allclose(da[dict(x=ind)].values, 0) + self.assertDataArrayIdentical(da['x'], get_data()['x']) + self.assertDataArrayIdentical(da['non-dim'], get_data()['non-dim']) + + da = get_data() + # conflict in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [0, 1, 2], + 'non-dim': ('x', [0, 2, 4])}) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da[dict(x=ind)] = value + + # consistent coordinate in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + da[dict(x=ind)] = value + assert np.allclose(da[dict(x=ind)].values, 0) + self.assertDataArrayIdentical(da['x'], get_data()['x']) + self.assertDataArrayIdentical(da['non-dim'], get_data()['non-dim']) + + # Conflict in the non-dimension coordinate + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + # conflict in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [0, 1, 2], + 'non-dim': ('x', [0, 2, 4])}) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da[dict(x=ind)] = value + + # consistent coordinate in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + da[dict(x=ind)] = value # should not raise + def test_contains(self): data_array = DataArray(1, coords={'x': 2}) with pytest.warns(FutureWarning): diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 192624b64f3..d0b6ed55f45 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -1001,15 +1001,12 @@ def test_isel_dataarray(self): # Conflict in the dimension coordinate indexing_da = DataArray(np.arange(1, 4), dims=['dim2'], coords={'dim2': np.random.randn(3)}) - with raises_regex( - IndexError, "dimension coordinate 'dim2'"): + with raises_regex(IndexError, "dimension coordinate 'dim2'"): actual = data.isel(dim2=indexing_da) # Also the case for DataArray - with raises_regex( - IndexError, "dimension coordinate 'dim2'"): + with raises_regex(IndexError, "dimension coordinate 'dim2'"): actual = data['var2'].isel(dim2=indexing_da) - with raises_regex( - IndexError, "dimension coordinate 'dim2'"): + with raises_regex(IndexError, "dimension coordinate 'dim2'"): data['dim2'].isel(dim2=indexing_da) # same name coordinate which does not conflict @@ -1502,7 +1499,7 @@ def test_reindex_like(self): expected = data.copy(deep=True) expected['dim3'] = ('dim3', list('cdefghijkl')) - expected['var3'][:-2] = expected['var3'][2:] + expected['var3'][:-2] = expected['var3'][2:].values expected['var3'][-2:] = np.nan expected['letters'] = expected['letters'].astype(object) expected['letters'][-2:] = np.nan @@ -1614,9 +1611,9 @@ def test_align(self): left = create_test_data() right = left.copy(deep=True) right['dim3'] = ('dim3', list('cdefghijkl')) - right['var3'][:-2] = right['var3'][2:] + right['var3'][:-2] = right['var3'][2:].values right['var3'][-2:] = np.random.randn(*right['var3'][-2:].shape) - right['numbers'][:-2] = right['numbers'][2:] + right['numbers'][:-2] = right['numbers'][2:].values right['numbers'][-2:] = -10 intersection = list('cdefghij') From 8e6e2e0ed4a57caef2238a155413c5abc67c4308 Mon Sep 17 00:00:00 2001 From: keisukefujii Date: Wed, 6 Dec 2017 16:06:20 +0900 Subject: [PATCH 06/13] Only pass a dict of variables to `assert_coordinate_consistent` --- xarray/core/coordinates.py | 3 +-- xarray/core/dataarray.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index ae9eef18249..50ca4377189 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -313,8 +313,7 @@ def assert_coordinate_consistent(obj, coords): for k in obj.dims: # make sure there are no conflict in dimension coordinates if (k in coords and k in obj.coords): - coord = getattr(coords[k], 'variable', coords[k]) # Variable - if not coord.equals(obj[k].variable): + if not coords[k].equals(obj[k].variable): raise IndexError( 'dimension coordinate {!r} conflicts between ' 'indexed and indexing objects:\n{}\nvs.\n{}' diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 7b82ce8d622..3327124358d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -487,7 +487,7 @@ def __setitem__(self, key, value): # Coordinates in key, value and self[key] should be consistent. obj = self[key] if isinstance(value, DataArray): - assert_coordinate_consistent(value, obj.coords) + assert_coordinate_consistent(value, obj._coords) # DataArray key -> Variable key key = {k: v.variable if isinstance(v, DataArray) else v for k, v in self._item_key_to_dict(key).items()} From aef3d56925b11cf5b4ff1c96018d81fbd4c49d03 Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Wed, 6 Dec 2017 21:02:17 +0900 Subject: [PATCH 07/13] Update docs. --- doc/indexing.rst | 15 ++++++++++++++- doc/whats-new.rst | 3 +++ xarray/core/dataarray.py | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/indexing.rst b/doc/indexing.rst index 74aef454c8d..c287fa85e01 100644 --- a/doc/indexing.rst +++ b/doc/indexing.rst @@ -277,7 +277,7 @@ Like numpy and pandas, xarray supports indexing many array elements at once in a If you only provide integers, slices, or unlabeled arrays (array without dimension names, such as ``np.ndarray``, ``list``, but not :py:meth:`~xarray.DataArray` or :py:meth:`~xarray.Variable`) indexing can be -understand as orthogonally. Each indexer component selects independently along +understood as orthogonally. Each indexer component selects independently along the corresponding dimension, similar to how vector indexing works in Fortran or MATLAB, or after using the :py:func:`numpy.ix_` helper: @@ -357,6 +357,13 @@ These methods may and also be applied to ``Dataset`` objects ``isel_points`` and ``sel_points`` are now deprecated. See :ref:`more_advanced_indexing` for their alternative. +.. note:: + + If an indexer is a :py:meth:`~xarray.DataArray`, its coordinates should not + conflict with the selected subpart of the target array. + Otherwise, ``IndexError`` will be raised. + + .. _assigning_values: Assigning values with indexing @@ -401,6 +408,11 @@ __ https://docs.scipy.org/doc/numpy/user/basics.indexing.html#assigning-values-t Dask array does not support value assignment (see :ref:`dask` for the details). +.. note:: + + Coordinates in both the left- and right-hand-side arrays should not + conflict with each other. + Otherwise, ``IndexError`` will be raised. .. warning:: @@ -457,6 +469,7 @@ method: arr.sel(space=xr.DataArray(['IA', 'IL', 'IN'], dims=['new_time']), time=times) + .. _align and reindex: Align and reindex diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 5c1725ece61..b3bd582fa49 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -29,6 +29,9 @@ Bug fixes ~~~~~~~~~ - Bug fix in vectorized assignment (:issue:`1743`, `1744`). + Now item assignment to :py:meth:`~DataArray.__setitem__` checks + coordinates of target, destination and keys. If there are any conflict among + these coordinates, ``IndexError`` will be raised. By `Keisuke Fujii `_. diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 3327124358d..f14c98b9f07 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -487,7 +487,7 @@ def __setitem__(self, key, value): # Coordinates in key, value and self[key] should be consistent. obj = self[key] if isinstance(value, DataArray): - assert_coordinate_consistent(value, obj._coords) + assert_coordinate_consistent(value, obj.coords.variables) # DataArray key -> Variable key key = {k: v.variable if isinstance(v, DataArray) else v for k, v in self._item_key_to_dict(key).items()} From f10ecf48f581942ade27cece677a20976bfc0c54 Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 10:00:34 +0900 Subject: [PATCH 08/13] still working --- xarray/core/dataarray.py | 11 ++++--- xarray/core/dataset.py | 43 +++++++++++++++------------ xarray/tests/test_dataarray.py | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index f14c98b9f07..b267eb2b62a 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -102,22 +102,21 @@ class _LocIndexer(object): def __init__(self, data_array): self.data_array = data_array - def _remap_key(self, key): + def __getitem__(self, key): if not utils.is_dict_like(key): # expand the indexer so we can handle Ellipsis labels = indexing.expanded_indexer(key, self.data_array.ndim) key = dict(zip(self.data_array.dims, labels)) - return indexing.remap_label_indexers(self.data_array, key) + return self.data_array.sel(**key) - def __getitem__(self, key): + def __setitem__(self, key, value): if not utils.is_dict_like(key): # expand the indexer so we can handle Ellipsis labels = indexing.expanded_indexer(key, self.data_array.ndim) key = dict(zip(self.data_array.dims, labels)) - return self.data_array.sel(**key) - def __setitem__(self, key, value): - pos_indexers, _ = self._remap_key(key) + ds = self.data_array._to_temp_dataset() + pos_indexers, _ = ds._remap_label_indexers(**key) self.data_array[pos_indexers] = value diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 7867484f748..7c3f035aec3 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1371,6 +1371,28 @@ def isel(self, drop=False, **indexers): .union(coord_vars)) return self._replace_vars_and_dims(variables, coord_names=coord_names) + def _remap_label_indexers(self, method=None, tolerance=None, **indexers): + from .dataarray import DataArray + + v_indexers = {k: v.variable.data if isinstance(v, DataArray) else v + for k, v in indexers.items()} + + pos_indexers, new_indexes = indexing.remap_label_indexers( + self, v_indexers, method=method, tolerance=tolerance + ) + # attach indexer's coordinate to pos_indexers + for k, v in indexers.items(): + if isinstance(v, Variable): + pos_indexers[k] = Variable(v.dims, pos_indexers[k]) + elif isinstance(v, DataArray): + # drop coordinates found in indexers since .sel() already + # ensures alignments + coords = OrderedDict((k, v) for k, v in v._coords.items() + if k not in indexers) + pos_indexers[k] = DataArray(pos_indexers[k], + coords=coords, dims=v.dims) + return pos_indexers, new_indexes + def sel(self, method=None, tolerance=None, drop=False, **indexers): """Returns a new dataset with each array indexed by tick labels along the specified dimension(s). @@ -1430,25 +1452,8 @@ def sel(self, method=None, tolerance=None, drop=False, **indexers): Dataset.isel DataArray.sel """ - from .dataarray import DataArray - - v_indexers = {k: v.variable.data if isinstance(v, DataArray) else v - for k, v in indexers.items()} - - pos_indexers, new_indexes = indexing.remap_label_indexers( - self, v_indexers, method=method, tolerance=tolerance - ) - # attach indexer's coordinate to pos_indexers - for k, v in indexers.items(): - if isinstance(v, Variable): - pos_indexers[k] = Variable(v.dims, pos_indexers[k]) - elif isinstance(v, DataArray): - # drop coordinates found in indexers since .sel() already - # ensures alignments - coords = OrderedDict((k, v) for k, v in v._coords.items() - if k not in indexers) - pos_indexers[k] = DataArray(pos_indexers[k], - coords=coords, dims=v.dims) + pos_indexers, new_indexes = self._remap_label_indexers( + method, tolerance, **indexers) result = self.isel(drop=drop, **pos_indexers) return result._replace_indexes(new_indexes) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 89a1038b24b..4f2c3132e32 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -598,6 +598,8 @@ def get_data(): value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], coords={'x': [1, 2, 3], 'non-dim': ('x', [0, 2, 4])}) + da[dict(x=ind)] = value # should not raise + # conflict in the assigning values value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], coords={'x': [0, 1, 2], @@ -922,6 +924,58 @@ def test_loc_assign(self): self.assertTrue(np.all(da.values[0] == np.zeros(4))) assert da.values[1, 0] != 0 + def test_loc_assign_dataarray(self): + def get_data(): + return DataArray(np.ones((4, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': np.arange(4), 'y': ['a', 'b', 'c'], + 'non-dim': ('x', [1, 3, 4, 2])}) + + da = get_data() + # indexer with inconsistent coordinates. + ind = DataArray(np.arange(1, 4), dims=['x'], + coords={'x': np.random.randn(3)}) + da.sel(x=ind) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da.loc[dict(x=ind)] = 0 + + # indexer with consistent coordinates. + ind = DataArray(np.arange(1, 4), dims=['x'], + coords={'x': np.arange(1, 4)}) + da.loc[dict(x=ind)] = 0 # should not raise + assert np.allclose(da[dict(x=ind)].values, 0) + self.assertDataArrayIdentical(da['x'], get_data()['x']) + self.assertDataArrayIdentical(da['non-dim'], get_data()['non-dim']) + + da = get_data() + # conflict in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [0, 1, 2], + 'non-dim': ('x', [0, 2, 4])}) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da.loc[dict(x=ind)] = value + + # consistent coordinate in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + da.loc[dict(x=ind)] = value + assert np.allclose(da[dict(x=ind)].values, 0) + self.assertDataArrayIdentical(da['x'], get_data()['x']) + self.assertDataArrayIdentical(da['non-dim'], get_data()['non-dim']) + + # Conflict in the non-dimension coordinate + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + with raises_regex(IndexError, "dimension coordinate 'x'"): + da[dict(x=ind)] = value + + # consistent coordinate in the assigning values + value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], + coords={'x': [1, 2, 3], + 'non-dim': ('x', [0, 2, 4])}) + da[dict(x=ind)] = value # should not raise + def test_loc_single_boolean(self): data = DataArray([0, 1], coords=[[True, False]]) self.assertEqual(data.loc[True], 0) From d482f8012e4c780f9b49f52e55b83dc8bb1b1820 Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 10:06:43 +0900 Subject: [PATCH 09/13] Coordinate validation in .loc.__setitem__ --- xarray/tests/test_dataarray.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 4f2c3132e32..2a14742c948 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -932,10 +932,9 @@ def get_data(): da = get_data() # indexer with inconsistent coordinates. - ind = DataArray(np.arange(1, 4), dims=['x'], - coords={'x': np.random.randn(3)}) - da.sel(x=ind) - with raises_regex(IndexError, "dimension coordinate 'x'"): + ind = DataArray(np.arange(1, 4), dims=['y'], + coords={'y': np.random.randn(3)}) + with raises_regex(IndexError, "dimension coordinate 'y'"): da.loc[dict(x=ind)] = 0 # indexer with consistent coordinates. @@ -963,19 +962,6 @@ def get_data(): self.assertDataArrayIdentical(da['x'], get_data()['x']) self.assertDataArrayIdentical(da['non-dim'], get_data()['non-dim']) - # Conflict in the non-dimension coordinate - value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], - coords={'x': [1, 2, 3], - 'non-dim': ('x', [0, 2, 4])}) - with raises_regex(IndexError, "dimension coordinate 'x'"): - da[dict(x=ind)] = value - - # consistent coordinate in the assigning values - value = xr.DataArray(np.zeros((3, 3, 2)), dims=['x', 'y', 'z'], - coords={'x': [1, 2, 3], - 'non-dim': ('x', [0, 2, 4])}) - da[dict(x=ind)] = value # should not raise - def test_loc_single_boolean(self): data = DataArray([0, 1], coords=[[True, False]]) self.assertEqual(data.loc[True], 0) From c2b5ac390f53ab16c5c39f370d604dd1af8365ec Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 10:08:38 +0900 Subject: [PATCH 10/13] remove extra parenthesis --- xarray/core/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index 50ca4377189..91a99b23d48 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -312,7 +312,7 @@ def assert_coordinate_consistent(obj, coords): """ for k in obj.dims: # make sure there are no conflict in dimension coordinates - if (k in coords and k in obj.coords): + if k in coords and k in obj.coords: if not coords[k].equals(obj[k].variable): raise IndexError( 'dimension coordinate {!r} conflicts between ' From 84e5e6fd2c5c0faabe5f7b9dd2324967959fa0bf Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 10:33:08 +0900 Subject: [PATCH 11/13] Refactoring. remap_label_indexers is moved to coordinates.py --- xarray/core/coordinates.py | 37 ++++++++++++++++++++++++++++++++++++- xarray/core/dataarray.py | 6 +++--- xarray/core/dataset.py | 28 +++------------------------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index 91a99b23d48..6b83577a90a 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -5,11 +5,12 @@ from contextlib import contextmanager import pandas as pd -from . import formatting +from . import formatting, indexing from .utils import Frozen from .merge import ( merge_coords, expand_and_merge_variables, merge_coords_for_inplace_math) from .pycompat import OrderedDict +from .variable import Variable class AbstractCoordinates(Mapping, formatting.ReprMixin): @@ -318,3 +319,37 @@ def assert_coordinate_consistent(obj, coords): 'dimension coordinate {!r} conflicts between ' 'indexed and indexing objects:\n{}\nvs.\n{}' .format(k, obj[k], coords[k])) + + +def remap_label_indexers(obj, method=None, tolerance=None, **indexers): + """ + Remap **indexers from obj.coords. + If indexer is an instance of DataArray and it has coordinate, then this + coordinate will be attached to pos_indexers. + + Returns + ------- + pos_indexers: Same type of indexers. + np.ndarray or Variable or DataArra + new_indexes: mapping of new dimensional-coordinate. + """ + from .dataarray import DataArray + + v_indexers = {k: v.variable.data if isinstance(v, DataArray) else v + for k, v in indexers.items()} + + pos_indexers, new_indexes = indexing.remap_label_indexers( + obj, v_indexers, method=method, tolerance=tolerance + ) + # attach indexer's coordinate to pos_indexers + for k, v in indexers.items(): + if isinstance(v, Variable): + pos_indexers[k] = Variable(v.dims, pos_indexers[k]) + elif isinstance(v, DataArray): + # drop coordinates found in indexers since .sel() already + # ensures alignments + coords = OrderedDict((k, v) for k, v in v._coords.items() + if k not in indexers) + pos_indexers[k] = DataArray(pos_indexers[k], + coords=coords, dims=v.dims) + return pos_indexers, new_indexes diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index b267eb2b62a..3352529704d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -20,7 +20,8 @@ from .alignment import align, reindex_like_indexers from .common import AbstractArray, BaseDataObject from .coordinates import (DataArrayCoordinates, LevelCoordinatesSource, - Indexes, assert_coordinate_consistent) + Indexes, assert_coordinate_consistent, + remap_label_indexers) from .dataset import Dataset, merge_indexes, split_indexes from .pycompat import iteritems, basestring, OrderedDict, zip, range from .variable import (as_variable, Variable, as_compatible_data, @@ -115,8 +116,7 @@ def __setitem__(self, key, value): labels = indexing.expanded_indexer(key, self.data_array.ndim) key = dict(zip(self.data_array.dims, labels)) - ds = self.data_array._to_temp_dataset() - pos_indexers, _ = ds._remap_label_indexers(**key) + pos_indexers, _ = remap_label_indexers(self.data_array, **key) self.data_array[pos_indexers] = value diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 7c3f035aec3..fb1f01f0398 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -24,7 +24,7 @@ from .. import conventions from .alignment import align from .coordinates import (DatasetCoordinates, LevelCoordinatesSource, Indexes, - assert_coordinate_consistent) + assert_coordinate_consistent, remap_label_indexers) from .common import ImplementsDatasetReduce, BaseDataObject from .dtypes import is_datetime_like from .merge import (dataset_update_method, dataset_merge_method, @@ -1371,28 +1371,6 @@ def isel(self, drop=False, **indexers): .union(coord_vars)) return self._replace_vars_and_dims(variables, coord_names=coord_names) - def _remap_label_indexers(self, method=None, tolerance=None, **indexers): - from .dataarray import DataArray - - v_indexers = {k: v.variable.data if isinstance(v, DataArray) else v - for k, v in indexers.items()} - - pos_indexers, new_indexes = indexing.remap_label_indexers( - self, v_indexers, method=method, tolerance=tolerance - ) - # attach indexer's coordinate to pos_indexers - for k, v in indexers.items(): - if isinstance(v, Variable): - pos_indexers[k] = Variable(v.dims, pos_indexers[k]) - elif isinstance(v, DataArray): - # drop coordinates found in indexers since .sel() already - # ensures alignments - coords = OrderedDict((k, v) for k, v in v._coords.items() - if k not in indexers) - pos_indexers[k] = DataArray(pos_indexers[k], - coords=coords, dims=v.dims) - return pos_indexers, new_indexes - def sel(self, method=None, tolerance=None, drop=False, **indexers): """Returns a new dataset with each array indexed by tick labels along the specified dimension(s). @@ -1452,8 +1430,8 @@ def sel(self, method=None, tolerance=None, drop=False, **indexers): Dataset.isel DataArray.sel """ - pos_indexers, new_indexes = self._remap_label_indexers( - method, tolerance, **indexers) + pos_indexers, new_indexes = remap_label_indexers( + self, method, tolerance, **indexers) result = self.isel(drop=drop, **pos_indexers) return result._replace_indexes(new_indexes) From 2011140641a44a68b5e97c0b67e87c5f6e35e8da Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 10:35:18 +0900 Subject: [PATCH 12/13] Added an exception in doc for coordinate confliction --- doc/indexing.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/indexing.rst b/doc/indexing.rst index c287fa85e01..6b01471ecfb 100644 --- a/doc/indexing.rst +++ b/doc/indexing.rst @@ -360,7 +360,8 @@ These methods may and also be applied to ``Dataset`` objects .. note:: If an indexer is a :py:meth:`~xarray.DataArray`, its coordinates should not - conflict with the selected subpart of the target array. + conflict with the selected subpart of the target array (except for the + explicitly indexed dimensions with ``.loc``/``.sel``). Otherwise, ``IndexError`` will be raised. From 6906eebfc7645d06ee807773f5df9215634addef Mon Sep 17 00:00:00 2001 From: Keisuke Fujii Date: Fri, 8 Dec 2017 17:58:48 +0900 Subject: [PATCH 13/13] Added a TODO for unused indexing part in Loc.__setitem__ --- xarray/core/dataarray.py | 2 ++ xarray/core/dataset.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 3352529704d..7bc06143970 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -484,6 +484,8 @@ def __setitem__(self, key, value): self.coords[key] = value else: # Coordinates in key, value and self[key] should be consistent. + # TODO Coordinate consistency in key is checked here, but it + # causes unnecessary indexing. It should be optimized. obj = self[key] if isinstance(value, DataArray): assert_coordinate_consistent(value, obj.coords.variables) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index fb1f01f0398..681390f8504 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1430,8 +1430,8 @@ def sel(self, method=None, tolerance=None, drop=False, **indexers): Dataset.isel DataArray.sel """ - pos_indexers, new_indexes = remap_label_indexers( - self, method, tolerance, **indexers) + pos_indexers, new_indexes = remap_label_indexers(self, method, + tolerance, **indexers) result = self.isel(drop=drop, **pos_indexers) return result._replace_indexes(new_indexes)