From 15f32b76edfe072959316fa9618a4150cf87196b Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Thu, 19 Mar 2020 19:36:47 -0600 Subject: [PATCH 1/9] Bug: wrap scalar accessor in try/except block and generate a better error message (GH9259). --- pandas/core/indexing.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 8038bba8b6448..2b03c8b5fb790 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2022,16 +2022,24 @@ def _convert_key(self, key, is_setter: bool = False): raise AbstractMethodError(self) def __getitem__(self, key): - if not isinstance(key, tuple): + try: + if not isinstance(key, tuple): - # we could have a convertible item here (e.g. Timestamp) - if not is_list_like_indexer(key): - key = tuple([key]) - else: - raise ValueError("Invalid call for scalar access (getting)!") + # we could have a convertible item here (e.g. Timestamp) + if not is_list_like_indexer(key): + key = tuple([key]) + else: + raise ValueError("Invalid call for scalar access (getting)!") - key = self._convert_key(key) - return self.obj._get_value(*key, takeable=self._takeable) + key = self._convert_key(key) + return self.obj._get_value(*key, takeable=self._takeable) + except KeyError as err: + if isinstance(self.obj.index, ABCMultiIndex): + raise KeyError( + f"Detected KeyError {err}, indexing with {key} failing for MultiIndex" + ) from err + else: + raise def __setitem__(self, key, value): if isinstance(key, tuple): From 3ef72e62604a334835f9c5a02d0125ea742a1ec8 Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Thu, 19 Mar 2020 23:43:48 -0600 Subject: [PATCH 2/9] Bug: test exceptions (GH9259). --- pandas/tests/indexes/multi/test_get_set.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 675a1e2e832f3..7bb5c92fe42c8 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -413,3 +413,24 @@ def test_set_levels_with_iterable(): [expected_sizes, colors], names=["size", "color"] ) tm.assert_index_equal(result, expected) + + +def test_at_indexing_fails_multiindex(): + # GH9259 + cols = [("a_col", chr(i + 65)) for i in range(5)] + cols.extend([("b_col", chr(i + 65)) for i in range(5, 10)]) + idx = [("a_row", chr(i + 65)) for i in range(5)] + idx.extend([("b_row", chr(i + 65)) for i in range(5, 10)]) + df = pd.DataFrame( + np.linspace(1, 100, 100).reshape(10, 10), + index=pd.MultiIndex.from_tuples(idx), + columns=pd.MultiIndex.from_tuples(cols), + ) + s = pd.Series(np.linspace(1, 91, 10), index=pd.MultiIndex.from_tuples(idx)) + + msg = r".+indexing with .+ failing for MultiIndex" + with pytest.raises(KeyError, match=msg): + df.at["a_row", "A"] + + with pytest.raises(TypeError): + s.at["a_row", "A"] From 5ce133a214105ac453a419dddbd0f7d465b226c3 Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Thu, 19 Mar 2020 23:58:08 -0600 Subject: [PATCH 3/9] Bug: document changes (GH9259). --- doc/source/whatsnew/v1.1.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 720ce7af47a18..470f6cb99e117 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -304,6 +304,7 @@ Indexing - Bug in :meth:`DataFrame.iloc.__setitem__` on a :class:`DataFrame` with duplicate columns incorrectly setting values for all matching columns (:issue:`15686`, :issue:`22036`) - Bug in :meth:`DataFrame.loc:` and :meth:`Series.loc` with a :class:`DatetimeIndex`, :class:`TimedeltaIndex`, or :class:`PeriodIndex` incorrectly allowing lookups of non-matching datetime-like dtypes (:issue:`32650`) - Bug in :meth:`Series.__getitem__` indexing with non-standard scalars, e.g. ``np.dtype`` (:issue:`32684`) +- Calling :meth:`DataFrame.at` on a DataFrame with a MultiIndex raises an exception with a more informative message (:issue:`9259`) Missing ^^^^^^^ From 5a3a43d0221dd87ae629aa43e7e2dd7bc20e5145 Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Fri, 20 Mar 2020 00:07:10 -0600 Subject: [PATCH 4/9] Bug: improve raises (GH9259). --- pandas/tests/indexes/multi/test_get_set.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 7bb5c92fe42c8..0e445d56eb037 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -428,9 +428,8 @@ def test_at_indexing_fails_multiindex(): ) s = pd.Series(np.linspace(1, 91, 10), index=pd.MultiIndex.from_tuples(idx)) - msg = r".+indexing with .+ failing for MultiIndex" - with pytest.raises(KeyError, match=msg): + with pytest.raises(KeyError, match=r".+? indexing with .+? failing for MultiIndex"): df.at["a_row", "A"] - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=r".+? got multiple values for argument .+?"): s.at["a_row", "A"] From e89c7d62bfea688e633b3cae3bf98087f5e9586f Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Fri, 20 Mar 2020 00:11:50 -0600 Subject: [PATCH 5/9] Bug: flake8 fix (GH9259). --- pandas/core/indexing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 2b03c8b5fb790..2ad875eac3bfd 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2036,7 +2036,8 @@ def __getitem__(self, key): except KeyError as err: if isinstance(self.obj.index, ABCMultiIndex): raise KeyError( - f"Detected KeyError {err}, indexing with {key} failing for MultiIndex" + f"Detected KeyError {err}, indexing with {key} " + "failing for MultiIndex" ) from err else: raise From 957f77e85e591e53b145e3037ce2da0d98b7dab6 Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Sun, 22 Mar 2020 19:05:49 -0600 Subject: [PATCH 6/9] Bug: incorporate PR feedback (GH9259). --- pandas/core/indexing.py | 18 +++++++++--------- pandas/tests/indexes/multi/test_get_set.py | 10 ++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 2ad875eac3bfd..12d770e02b116 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2022,23 +2022,23 @@ def _convert_key(self, key, is_setter: bool = False): raise AbstractMethodError(self) def __getitem__(self, key): - try: - if not isinstance(key, tuple): + if not isinstance(key, tuple): - # we could have a convertible item here (e.g. Timestamp) - if not is_list_like_indexer(key): - key = tuple([key]) - else: - raise ValueError("Invalid call for scalar access (getting)!") + # we could have a convertible item here (e.g. Timestamp) + if not is_list_like_indexer(key): + key = tuple([key]) + else: + raise ValueError("Invalid call for scalar access (getting)!") - key = self._convert_key(key) + key = self._convert_key(key) + try: return self.obj._get_value(*key, takeable=self._takeable) except KeyError as err: if isinstance(self.obj.index, ABCMultiIndex): raise KeyError( f"Detected KeyError {err}, indexing with {key} " "failing for MultiIndex" - ) from err + ) else: raise diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 0e445d56eb037..0d1dbf4a01d48 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -417,16 +417,14 @@ def test_set_levels_with_iterable(): def test_at_indexing_fails_multiindex(): # GH9259 - cols = [("a_col", chr(i + 65)) for i in range(5)] - cols.extend([("b_col", chr(i + 65)) for i in range(5, 10)]) - idx = [("a_row", chr(i + 65)) for i in range(5)] - idx.extend([("b_row", chr(i + 65)) for i in range(5, 10)]) + cols = [("a_col", chr(i + 65)) for i in range(2)] + idx = [("a_row", chr(i + 65)) for i in range(2)] df = pd.DataFrame( - np.linspace(1, 100, 100).reshape(10, 10), + np.linspace(1, 4, 4).reshape(2, 2), index=pd.MultiIndex.from_tuples(idx), columns=pd.MultiIndex.from_tuples(cols), ) - s = pd.Series(np.linspace(1, 91, 10), index=pd.MultiIndex.from_tuples(idx)) + s = pd.Series(np.linspace(1, 2, 2), index=pd.MultiIndex.from_tuples(idx)) with pytest.raises(KeyError, match=r".+? indexing with .+? failing for MultiIndex"): df.at["a_row", "A"] From 90ac52b4efab7667523fd4812a1bf78265da1830 Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Thu, 16 Apr 2020 22:12:11 -0600 Subject: [PATCH 7/9] Fix single letter variable based on PR feedback. --- pandas/tests/indexes/multi/test_get_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 32a15eb17b50c..02ba118454f75 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -340,10 +340,10 @@ def test_at_indexing_fails_multiindex(): index=pd.MultiIndex.from_tuples(idx), columns=pd.MultiIndex.from_tuples(cols), ) - s = pd.Series(np.linspace(1, 2, 2), index=pd.MultiIndex.from_tuples(idx)) + ser = pd.Series(np.linspace(1, 2, 2), index=pd.MultiIndex.from_tuples(idx)) with pytest.raises(KeyError, match=r".+? indexing with .+? failing for MultiIndex"): df.at["a_row", "A"] with pytest.raises(TypeError, match=r".+? got multiple values for argument .+?"): - s.at["a_row", "A"] + ser.at["a_row", "A"] From 217a35a0c957bb9660c0560ff147e1437ef0caab Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Thu, 16 Apr 2020 22:14:45 -0600 Subject: [PATCH 8/9] Series from dataframe based on PR feedback. --- pandas/tests/indexes/multi/test_get_set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 02ba118454f75..eeb1aa2fade42 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -340,7 +340,7 @@ def test_at_indexing_fails_multiindex(): index=pd.MultiIndex.from_tuples(idx), columns=pd.MultiIndex.from_tuples(cols), ) - ser = pd.Series(np.linspace(1, 2, 2), index=pd.MultiIndex.from_tuples(idx)) + ser = df[("a_col", "A")] with pytest.raises(KeyError, match=r".+? indexing with .+? failing for MultiIndex"): df.at["a_row", "A"] From b792fea6eaadf3519b551aaf54493951dfbbcf7c Mon Sep 17 00:00:00 2001 From: Ayla Khan Date: Sun, 31 May 2020 18:59:59 -0600 Subject: [PATCH 9/9] Remove series from test. --- pandas/tests/indexes/multi/test_get_set.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index eeb1aa2fade42..9ca3fbc615e8d 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -340,10 +340,6 @@ def test_at_indexing_fails_multiindex(): index=pd.MultiIndex.from_tuples(idx), columns=pd.MultiIndex.from_tuples(cols), ) - ser = df[("a_col", "A")] with pytest.raises(KeyError, match=r".+? indexing with .+? failing for MultiIndex"): df.at["a_row", "A"] - - with pytest.raises(TypeError, match=r".+? got multiple values for argument .+?"): - ser.at["a_row", "A"]