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/core/indexing.py b/pandas/core/indexing.py index 3a146bb0438c5..ee0e9d32c4601 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 IndexingError: + 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 IndexingError: + 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 IndexingError("No label returned") def _getitem_axis(self, key, axis: int): key = item_from_zerodim(key) diff --git a/pandas/tests/series/indexing/test_multiindex.py b/pandas/tests/series/indexing/test_multiindex.py new file mode 100644 index 0000000000000..e98a32d62b767 --- /dev/null +++ b/pandas/tests/series/indexing/test_multiindex.py @@ -0,0 +1,22 @@ +""" test get/set & misc """ + + +import pandas as pd +from pandas import MultiIndex, Series + + +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