diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 48561b50f66ae..ad5af5df710ba 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -621,6 +621,7 @@ Indexing - Bug in :meth:`DataFrame.iloc` and :meth:`Series.iloc` aligning objects in ``__setitem__`` (:issue:`22046`) - Bug in :meth:`DataFrame.loc` did not raise ``KeyError`` when missing combination was given with ``slice(None)`` for remaining levels (:issue:`19556`) - Bug in :meth:`DataFrame.loc` raising ``TypeError`` when non-integer slice was given to select values from :class:`MultiIndex` (:issue:`25165`, :issue:`24263`) +- Bug in :meth:`DataFrame.loc` and :meth:`DataFrame.__getitem__` raising ``KeyError`` when columns were :class:`MultiIndex` with only one level (:issue:`29749`) Missing ^^^^^^^ diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6f6d94f0e9f8e..5b87c4ea8b9cc 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2935,7 +2935,7 @@ def __getitem__(self, key): if is_hashable(key): # shortcut if the key is in columns if self.columns.is_unique and key in self.columns: - if self.columns.nlevels > 1: + if isinstance(self.columns, MultiIndex): return self._getitem_multilevel(key) return self._get_item_cache(key) diff --git a/pandas/tests/frame/indexing/test_getitem.py b/pandas/tests/frame/indexing/test_getitem.py index 868df82a43a91..6c6b4e002644c 100644 --- a/pandas/tests/frame/indexing/test_getitem.py +++ b/pandas/tests/frame/indexing/test_getitem.py @@ -96,6 +96,17 @@ def test_getitem_callable(self, float_frame): expected = float_frame.iloc[[0, 2], :] tm.assert_frame_equal(result, expected) + def test_loc_multiindex_columns_one_level(self): + # GH#29749 + df = DataFrame([[1, 2]], columns=[["a", "b"]]) + expected = DataFrame([1], columns=[["a"]]) + + result = df["a"] + tm.assert_frame_equal(result, expected) + + result = df.loc[:, "a"] + tm.assert_frame_equal(result, expected) + class TestGetitemBooleanMask: def test_getitem_bool_mask_categorical_index(self):