From b376ca15e5bd6120222f65410d555079eaaf189b Mon Sep 17 00:00:00 2001 From: Belavin Denis Date: Fri, 26 Jun 2020 18:35:14 +0300 Subject: [PATCH 1/4] BUG-Fix: AssertionError when slicing MultiIndex and setting value of pandas.Series. --- pandas/core/indexes/base.py | 6 ------ pandas/core/indexes/multi.py | 6 +++++- pandas/tests/indexing/test_loc.py | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index b12a556a8291d..8f939c5e7dca5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -4946,12 +4946,6 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): """ start_slice, end_slice = self.slice_locs(start, end, step=step, kind=kind) - # return a slice - if not is_scalar(start_slice): - raise AssertionError("Start slice bound is non-scalar") - if not is_scalar(end_slice): - raise AssertionError("End slice bound is non-scalar") - return slice(start_slice, end_slice, step) def _maybe_cast_indexer(self, key): diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 15db6c51a1f2f..f8d2d0dd5eff0 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2890,7 +2890,11 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): # if we have a provided indexer, then this need not consider # the entire labels set - r = np.arange(start, stop, step) + if is_scalar(start) and is_scalar(stop): + r = np.arange(start, stop, step) + else: + r = np.all(start, stop, step) + if indexer is not None and len(indexer) != len(codes): # we have an indexer which maps the locations in the labels diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 47980e88f76d4..9838bdcfada72 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1119,3 +1119,20 @@ def test_loc_with_period_index_indexer(): tm.assert_frame_equal(df, df.loc[list(idx)]) tm.assert_frame_equal(df.iloc[0:5], df.loc[idx[0:5]]) tm.assert_frame_equal(df, df.loc[list(idx)]) + + +def test_loc_setting_value_at_multiindex(): + # GH 34870 + arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], + ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] + array = [1, 1, 1, 1, 1, 1, 1, 1] + answer = [1, 1, 100, 100, 100, 100, 1, 1] + + tuples = list(zip(*arrays)) + index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second']) + + dt1 = pd.Series(array, index=index) + dt2 = pd.Series(answer, index=index) + + dt1.loc[('baz', 'one'):('foo', 'two')] = 100 + tm.assert_series_equal(dt1, dt2) From 514c53185f435693e78b67a6311fd013cf58453a Mon Sep 17 00:00:00 2001 From: Belavin Denis Date: Fri, 3 Jul 2020 17:56:03 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=A1orrection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pandas/core/indexes/base.py | 7 +++++++ pandas/core/indexes/multi.py | 23 ++++++++++++----------- pandas/tests/indexing/test_loc.py | 10 ++++++---- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 8f939c5e7dca5..0f2fba741f7be 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -4946,6 +4946,13 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): """ start_slice, end_slice = self.slice_locs(start, end, step=step, kind=kind) + # return a slice + + if not is_scalar(start_slice): + raise AssertionError("Start slice bound is non-scalar") + if not is_scalar(end_slice): + raise AssertionError("End slice bound is non-scalar") + return slice(start_slice, end_slice, step) def _maybe_cast_indexer(self, key): diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index f8d2d0dd5eff0..82fec4ebe838b 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2890,11 +2890,7 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): # if we have a provided indexer, then this need not consider # the entire labels set - if is_scalar(start) and is_scalar(stop): - r = np.arange(start, stop, step) - else: - r = np.all(start, stop, step) - + r = np.arange(start, stop, step) if indexer is not None and len(indexer) != len(codes): # we have an indexer which maps the locations in the labels @@ -2933,12 +2929,17 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): step = key.step except KeyError: - # we have a partial slice (like looking up a partial date - # string) - start = stop = level_index.slice_indexer( - key.start, key.stop, key.step, kind="loc" - ) - step = start.step + if isinstance(key.start, tuple) or isinstance(key.stop, tuple): + return convert_indexer(key.start, key.stop, key.step) + + else: + # we have a partial slice (like looking up a partial date + # string) + + start = stop = level_index.slice_indexer( + key.start, key.stop, key.step, kind="loc" + ) + step = start.step if isinstance(start, slice) or isinstance(stop, slice): # we have a slice for start and/or stop diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 9838bdcfada72..39bfec6b8fc70 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1123,16 +1123,18 @@ def test_loc_with_period_index_indexer(): def test_loc_setting_value_at_multiindex(): # GH 34870 - arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], - ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] + arrays = [ + ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"], + ["one", "two", "one", "two", "one", "two", "one", "two"], + ] array = [1, 1, 1, 1, 1, 1, 1, 1] answer = [1, 1, 100, 100, 100, 100, 1, 1] tuples = list(zip(*arrays)) - index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second']) + index = pd.MultiIndex.from_tuples(tuples, names=["first", "second"]) dt1 = pd.Series(array, index=index) dt2 = pd.Series(answer, index=index) - dt1.loc[('baz', 'one'):('foo', 'two')] = 100 + dt1.loc[("baz", "one"):("foo", "two")] = 100 tm.assert_series_equal(dt1, dt2) From 7b320c3e392509cb06dcf38e82cea4cabf09ac37 Mon Sep 17 00:00:00 2001 From: Belavin Denis Date: Tue, 11 Aug 2020 16:40:48 +0300 Subject: [PATCH 3/4] fix names --- pandas/tests/indexing/test_loc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 39bfec6b8fc70..c1a37d46c9816 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1133,8 +1133,8 @@ def test_loc_setting_value_at_multiindex(): tuples = list(zip(*arrays)) index = pd.MultiIndex.from_tuples(tuples, names=["first", "second"]) - dt1 = pd.Series(array, index=index) - dt2 = pd.Series(answer, index=index) + result = pd.Series(array, index=index) + expected = pd.Series(answer, index=index) - dt1.loc[("baz", "one"):("foo", "two")] = 100 - tm.assert_series_equal(dt1, dt2) + result.loc[("baz", "one"):("foo", "two")] = 100 + tm.assert_series_equal(result, expected) From 1903c1e980f7d0156d46c46c50427ab61298bbc0 Mon Sep 17 00:00:00 2001 From: Belavin Denis Date: Mon, 17 Aug 2020 19:31:39 +0300 Subject: [PATCH 4/4] fix --- pandas/core/indexes/multi.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 82fec4ebe838b..4a062d22a13c5 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2928,18 +2928,9 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): stop = len(level_index) - 1 step = key.step except KeyError: - - if isinstance(key.start, tuple) or isinstance(key.stop, tuple): - return convert_indexer(key.start, key.stop, key.step) - - else: - # we have a partial slice (like looking up a partial date - # string) - - start = stop = level_index.slice_indexer( - key.start, key.stop, key.step, kind="loc" - ) - step = start.step + start = key.start + stop = key.stop + step = start.step if isinstance(start, slice) or isinstance(stop, slice): # we have a slice for start and/or stop