From 0c02a7f60bcd5e43561ac42f0d81ffa94f375970 Mon Sep 17 00:00:00 2001 From: pedrooa Date: Thu, 28 May 2020 23:14:08 -0300 Subject: [PATCH 1/8] BUG: Fixes Issue #34318 --- pandas/core/indexing.py | 14 +++++++++----- pandas/tests/series/indexing/test_indexing.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 3a146bb0438c5..7093feddcd67f 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -766,9 +766,11 @@ def _getitem_lowerdim(self, tup: Tuple): # ...but iloc should handle the tuple as simple integer-location # instead of checking it as multiindex representation (GH 13797) if isinstance(ax0, ABCMultiIndex) and self.name != "iloc": - result = self._handle_lowerdim_multi_index_axis0(tup) - if result is not None: + try: + result = self._handle_lowerdim_multi_index_axis0(tup) return result + except Exception: + pass if len(tup) > self.ndim: raise IndexingError("Too many indexers. handle elsewhere") @@ -816,9 +818,11 @@ def _getitem_nested_tuple(self, tup: Tuple): if self.name != "loc": # This should never be reached, but lets be explicit about it raise ValueError("Too many indices") - result = self._handle_lowerdim_multi_index_axis0(tup) - if result is not None: + try: + result = self._handle_lowerdim_multi_index_axis0(tup) return result + except Exception: + pass # this is a series with a multi-index specified a tuple of # selectors @@ -1065,7 +1069,7 @@ def _handle_lowerdim_multi_index_axis0(self, tup: Tuple): if len(tup) <= self.obj.index.nlevels and len(tup) > self.ndim: raise ek - return None + raise Exception("No label returned") def _getitem_axis(self, key, axis: int): key = item_from_zerodim(key) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 737e21af9242f..c4c8f79e35b67 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -853,6 +853,17 @@ def test_setitem_slice_into_readonly_backing_data(): assert not array.any() +def test_access_none_value_in_multiindex(): + # GH34318: test that you can access a None value using .loc through a Multiindex + + expected = None + result = pd.Series([None], pd.MultiIndex.from_arrays([["Level1"], ["Level2"]])).loc[ + ("Level1", "Level2") + ] + + assert expected == result + + """ miscellaneous methods """ From 2db8090b77b857cf09dda2cee31bcbcbda998d71 Mon Sep 17 00:00:00 2001 From: pedrooa Date: Mon, 1 Jun 2020 18:31:38 -0300 Subject: [PATCH 2/8] testing integration --- pandas/core/indexing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 7093feddcd67f..b5db94db144b7 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -822,6 +822,7 @@ def _getitem_nested_tuple(self, tup: Tuple): result = self._handle_lowerdim_multi_index_axis0(tup) return result except Exception: + print("ENTREI") pass # this is a series with a multi-index specified a tuple of From 537fc4557bc23863cb4af7b040dc78d1995665cf Mon Sep 17 00:00:00 2001 From: pedrooa Date: Mon, 1 Jun 2020 20:57:05 -0300 Subject: [PATCH 3/8] BUG: change Exception to IndexingError --- pandas/core/indexing.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index b5db94db144b7..ee0e9d32c4601 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -769,7 +769,7 @@ def _getitem_lowerdim(self, tup: Tuple): try: result = self._handle_lowerdim_multi_index_axis0(tup) return result - except Exception: + except IndexingError: pass if len(tup) > self.ndim: @@ -821,8 +821,7 @@ def _getitem_nested_tuple(self, tup: Tuple): try: result = self._handle_lowerdim_multi_index_axis0(tup) return result - except Exception: - print("ENTREI") + except IndexingError: pass # this is a series with a multi-index specified a tuple of @@ -1070,7 +1069,7 @@ def _handle_lowerdim_multi_index_axis0(self, tup: Tuple): if len(tup) <= self.obj.index.nlevels and len(tup) > self.ndim: raise ek - raise Exception("No label returned") + raise IndexingError("No label returned") def _getitem_axis(self, key, axis: int): key = item_from_zerodim(key) From 8a540fffbf3799d4e9ebe891e6bb08ebb9e91625 Mon Sep 17 00:00:00 2001 From: pedrooa Date: Tue, 2 Jun 2020 11:50:47 -0300 Subject: [PATCH 4/8] BUG: more tests and whats new note added --- doc/source/whatsnew/v1.1.0.rst | 1 + pandas/tests/series/indexing/test_indexing.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 5ef1f9dea5091..9c00bc5ea7434 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -811,6 +811,7 @@ Indexing - Bug in :meth:`DataFrame.truncate` and :meth:`Series.truncate` where index was assumed to be monotone increasing (:issue:`33756`) - Indexing with a list of strings representing datetimes failed on :class:`DatetimeIndex` or :class:`PeriodIndex`(:issue:`11278`) - Bug in :meth:`Series.at` when used with a :class:`MultiIndex` would raise an exception on valid inputs (:issue:`26989`) +- Bug in :meth:`Series.loc` when used with a :class:`MultiIndex` would raise an IndexingError when accessing a None value (:issue:`34318`) Missing ^^^^^^^ diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index c4c8f79e35b67..a2c8f5140361f 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -856,12 +856,18 @@ def test_setitem_slice_into_readonly_backing_data(): def test_access_none_value_in_multiindex(): # GH34318: test that you can access a None value using .loc through a Multiindex - expected = None - result = pd.Series([None], pd.MultiIndex.from_arrays([["Level1"], ["Level2"]])).loc[ - ("Level1", "Level2") - ] - - assert expected == result + s = Series([None], pd.MultiIndex.from_arrays([["Level1"], ["Level2"]])) + result = s.loc[("Level1", "Level2")] + assert result is None + + midx = MultiIndex.from_product([['Level1'], ['Level2_a', 'Level2_b']]) + s = Series([None] * len(midx), dtype=object, index=midx) + result = s.loc[('Level1', 'Level2_a')] + assert result is None + + s = Series([1] * len(midx), dtype=object, index=midx) + result = s.loc[('Level1', 'Level2_a')] + assert result == 1 """ From 6cab4694b379ced3067100b1772b50a3ba8f0880 Mon Sep 17 00:00:00 2001 From: pedrooa Date: Tue, 2 Jun 2020 11:55:00 -0300 Subject: [PATCH 5/8] BUG: fix code formatting --- pandas/tests/series/indexing/test_indexing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index a2c8f5140361f..bcc24b5376940 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -860,13 +860,13 @@ def test_access_none_value_in_multiindex(): result = s.loc[("Level1", "Level2")] assert result is None - midx = MultiIndex.from_product([['Level1'], ['Level2_a', 'Level2_b']]) + midx = MultiIndex.from_product([["Level1"], ["Level2_a", "Level2_b"]]) s = Series([None] * len(midx), dtype=object, index=midx) - result = s.loc[('Level1', 'Level2_a')] + result = s.loc[("Level1", "Level2_a")] assert result is None s = Series([1] * len(midx), dtype=object, index=midx) - result = s.loc[('Level1', 'Level2_a')] + result = s.loc[("Level1", "Level2_a")] assert result == 1 From 25800c6778b1c2b4ffec10848645bccfbf1380c9 Mon Sep 17 00:00:00 2001 From: pedrooa Date: Tue, 2 Jun 2020 20:16:24 -0300 Subject: [PATCH 6/8] new file for multiindex tests --- .../tests/series/indexing/test_multiindex.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 pandas/tests/series/indexing/test_multiindex.py diff --git a/pandas/tests/series/indexing/test_multiindex.py b/pandas/tests/series/indexing/test_multiindex.py new file mode 100644 index 0000000000000..f28c4bdc05c79 --- /dev/null +++ b/pandas/tests/series/indexing/test_multiindex.py @@ -0,0 +1,42 @@ +""" test get/set & misc """ + +from datetime import timedelta + +import numpy as np +import pytest + +from pandas.core.dtypes.common import is_scalar + +import pandas as pd +from pandas import ( + Categorical, + DataFrame, + IndexSlice, + MultiIndex, + Series, + Timedelta, + Timestamp, + date_range, + period_range, + timedelta_range, +) +import pandas._testing as tm + +from pandas.tseries.offsets import BDay + + +def test_access_none_value_in_multiindex(): + # GH34318: test that you can access a None value using .loc through a Multiindex + + s = Series([None], pd.MultiIndex.from_arrays([["Level1"], ["Level2"]])) + result = s.loc[("Level1", "Level2")] + assert result is None + + midx = MultiIndex.from_product([["Level1"], ["Level2_a", "Level2_b"]]) + s = Series([None] * len(midx), dtype=object, index=midx) + result = s.loc[("Level1", "Level2_a")] + assert result is None + + s = Series([1] * len(midx), dtype=object, index=midx) + result = s.loc[("Level1", "Level2_a")] + assert result == 1 From 0bc5844e798503a49501d1159ad99c2c3e1a838e Mon Sep 17 00:00:00 2001 From: pedrooa Date: Tue, 2 Jun 2020 20:17:39 -0300 Subject: [PATCH 7/8] relocating multiindex test --- pandas/tests/series/indexing/test_indexing.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index bcc24b5376940..737e21af9242f 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -853,23 +853,6 @@ def test_setitem_slice_into_readonly_backing_data(): assert not array.any() -def test_access_none_value_in_multiindex(): - # GH34318: test that you can access a None value using .loc through a Multiindex - - s = Series([None], pd.MultiIndex.from_arrays([["Level1"], ["Level2"]])) - result = s.loc[("Level1", "Level2")] - assert result is None - - midx = MultiIndex.from_product([["Level1"], ["Level2_a", "Level2_b"]]) - s = Series([None] * len(midx), dtype=object, index=midx) - result = s.loc[("Level1", "Level2_a")] - assert result is None - - s = Series([1] * len(midx), dtype=object, index=midx) - result = s.loc[("Level1", "Level2_a")] - assert result == 1 - - """ miscellaneous methods """ From 553a8798e54f4079a1e6f2a6910adb2100e50a7f Mon Sep 17 00:00:00 2001 From: pedrooa Date: Wed, 3 Jun 2020 00:12:47 -0300 Subject: [PATCH 8/8] Fix code formatting --- .../tests/series/indexing/test_multiindex.py | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/pandas/tests/series/indexing/test_multiindex.py b/pandas/tests/series/indexing/test_multiindex.py index f28c4bdc05c79..e98a32d62b767 100644 --- a/pandas/tests/series/indexing/test_multiindex.py +++ b/pandas/tests/series/indexing/test_multiindex.py @@ -1,28 +1,8 @@ """ test get/set & misc """ -from datetime import timedelta - -import numpy as np -import pytest - -from pandas.core.dtypes.common import is_scalar import pandas as pd -from pandas import ( - Categorical, - DataFrame, - IndexSlice, - MultiIndex, - Series, - Timedelta, - Timestamp, - date_range, - period_range, - timedelta_range, -) -import pandas._testing as tm - -from pandas.tseries.offsets import BDay +from pandas import MultiIndex, Series def test_access_none_value_in_multiindex():