From e46323ac4e1a86b15ee898fe9754aaeea39864c1 Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 5 Sep 2020 14:29:21 -0400 Subject: [PATCH 01/10] REGR: Series access with Index of tuples/frozenset --- pandas/core/series.py | 25 +++++++++---------- pandas/tests/series/indexing/test_indexing.py | 24 ++++++++++++++++++ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 9d84ce4b9ab2e..5d7cb99224e88 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -887,21 +887,20 @@ def __getitem__(self, key): elif key_is_scalar: return self._get_value(key) - if ( - isinstance(key, tuple) - and is_hashable(key) - and isinstance(self.index, MultiIndex) - ): + # if isinstance(key, tuple) and is_hashable(key): + if not is_iterator(key) and is_hashable(key): # Otherwise index.get_value will raise InvalidIndexError - try: - result = self._get_value(key) - - return result + if isinstance(self.index, MultiIndex): + try: + result = self._get_value(key) + return result - except KeyError: - # We still have the corner case where this tuple is a key - # in the first level of our MultiIndex - return self._get_values_tuple(key) + except KeyError: + # We still have the corner case where this tuple is a key + # in the first level of our MultiIndex + return self._get_values_tuple(key) + else: + return self._get_value(key) if is_iterator(key): key = list(key) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 3ed25b8bca566..373097713e6fb 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -1,6 +1,7 @@ """ test get/set & misc """ from datetime import timedelta +import re import numpy as np import pytest @@ -942,3 +943,26 @@ def assert_slices_equivalent(l_slc, i_slc): for key2 in [keystr2, box(keystr2)]: assert_slices_equivalent(SLC[key2:key:-1], SLC[13:8:-1]) assert_slices_equivalent(SLC[key:key2:-1], SLC[0:0:-1]) + + +def test_tuple_index(): + # GH 35534 - Selecting values when a Series has an Index of tuples + s = pd.Series([1, 2], index=[("a",), ("b",)]) + assert s[("a",)] == 1 + assert s[("b",)] == 2 + s[("b",)] = 3 + assert s[("b",)] == 3 + with pytest.raises(KeyError, match="('c',)"): + s[("c",)] + + +def test_frozenset_index(): + # GH35747 - Selecting values when a Series has an Index of frozenset + idx0, idx1 = frozenset("a"), frozenset("b") + s = pd.Series([1, 2], index=[idx0, idx1]) + assert s[idx0] == 1 + assert s[idx1] == 2 + s[idx1] = 3 + assert s[idx1] == 3 + with pytest.raises(KeyError, match=re.escape("frozenset({'c'})")): + s[frozenset("c")] From 1a373b9be1a8176d12a4f38ad2d07a1bc83d59e5 Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 5 Sep 2020 15:59:12 -0400 Subject: [PATCH 02/10] whatsnew --- doc/source/whatsnew/v1.1.2.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.2.rst b/doc/source/whatsnew/v1.1.2.rst index d1a66256454ca..c093494eb0a94 100644 --- a/doc/source/whatsnew/v1.1.2.rst +++ b/doc/source/whatsnew/v1.1.2.rst @@ -21,7 +21,8 @@ Fixed regressions - Regression in :meth:`DataFrame.replace` where a ``TypeError`` would be raised when attempting to replace elements of type :class:`Interval` (:issue:`35931`) - Fix regression in pickle roundtrip of the ``closed`` attribute of :class:`IntervalIndex` (:issue:`35658`) - Fixed regression in :meth:`DataFrameGroupBy.agg` where a ``ValueError: buffer source array is read-only`` would be raised when the underlying array is read-only (:issue:`36014`) -- +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple. (:issue:`35534`) +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset. (:issue:`35747`) .. --------------------------------------------------------------------------- From aa2d1d4e473bd39826ec75f46248192107d012fe Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 5 Sep 2020 16:01:19 -0400 Subject: [PATCH 03/10] Cleanup --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 5d7cb99224e88..96be63d50fc83 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -887,7 +887,6 @@ def __getitem__(self, key): elif key_is_scalar: return self._get_value(key) - # if isinstance(key, tuple) and is_hashable(key): if not is_iterator(key) and is_hashable(key): # Otherwise index.get_value will raise InvalidIndexError if isinstance(self.index, MultiIndex): @@ -900,6 +899,7 @@ def __getitem__(self, key): # in the first level of our MultiIndex return self._get_values_tuple(key) else: + # No fallback for an Index return self._get_value(key) if is_iterator(key): From b9c98451cc35e14d611618772a011ce05c3093aa Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 7 Sep 2020 09:08:49 -0400 Subject: [PATCH 04/10] Better fix; removed test for non-existent key --- pandas/core/series.py | 20 +++++++++---------- pandas/tests/series/indexing/test_indexing.py | 4 ---- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 96be63d50fc83..32d40e6a0e9b3 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -887,20 +887,18 @@ def __getitem__(self, key): elif key_is_scalar: return self._get_value(key) - if not is_iterator(key) and is_hashable(key): + if is_hashable(key): # Otherwise index.get_value will raise InvalidIndexError - if isinstance(self.index, MultiIndex): - try: - result = self._get_value(key) - return result + try: + result = self._get_value(key) - except KeyError: - # We still have the corner case where this tuple is a key - # in the first level of our MultiIndex + return result + + except KeyError: + if isinstance(key, tuple) and isinstance(self.index, MultiIndex): + # We still have the corner case where a tuple is a key + # in the first level of our MultiIndex return self._get_values_tuple(key) - else: - # No fallback for an Index - return self._get_value(key) if is_iterator(key): key = list(key) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 373097713e6fb..e7cf0f5adb4ee 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -952,8 +952,6 @@ def test_tuple_index(): assert s[("b",)] == 2 s[("b",)] = 3 assert s[("b",)] == 3 - with pytest.raises(KeyError, match="('c',)"): - s[("c",)] def test_frozenset_index(): @@ -964,5 +962,3 @@ def test_frozenset_index(): assert s[idx1] == 2 s[idx1] = 3 assert s[idx1] == 3 - with pytest.raises(KeyError, match=re.escape("frozenset({'c'})")): - s[frozenset("c")] From 13cdba3dac999df874ba639c8af3f5adc7b3e4cf Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 7 Sep 2020 09:10:44 -0400 Subject: [PATCH 05/10] Removed unused import --- pandas/tests/series/indexing/test_indexing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index e7cf0f5adb4ee..a7e72ec423dcb 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -1,7 +1,6 @@ """ test get/set & misc """ from datetime import timedelta -import re import numpy as np import pytest From 0a986074fd2176f34cb5e2b1f7321b505b2d28f7 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 7 Sep 2020 09:27:12 -0400 Subject: [PATCH 06/10] Only call _get_values_tuple when there is a MultiIndex --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 32d40e6a0e9b3..b8ec00fb4d302 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -922,7 +922,7 @@ def _get_with(self, key): "Indexing a Series with DataFrame is not " "supported, use the appropriate DataFrame column" ) - elif isinstance(key, tuple): + elif isinstance(key, tuple) and isinstance(self.index, MultiIndex): return self._get_values_tuple(key) elif not is_list_like(key): From ba36e40dc9341d253e2461299ec1785fa2f3fe27 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 7 Sep 2020 09:56:50 -0400 Subject: [PATCH 07/10] Reverted downstream changes, new error message --- doc/source/whatsnew/v1.1.2.rst | 1 + pandas/core/series.py | 8 +++++--- pandas/tests/series/indexing/test_indexing.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v1.1.2.rst b/doc/source/whatsnew/v1.1.2.rst index c093494eb0a94..6c4220f3246c6 100644 --- a/doc/source/whatsnew/v1.1.2.rst +++ b/doc/source/whatsnew/v1.1.2.rst @@ -23,6 +23,7 @@ Fixed regressions - Fixed regression in :meth:`DataFrameGroupBy.agg` where a ``ValueError: buffer source array is read-only`` would be raised when the underlying array is read-only (:issue:`36014`) - Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple. (:issue:`35534`) - Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset. (:issue:`35747`) +- .. --------------------------------------------------------------------------- diff --git a/pandas/core/series.py b/pandas/core/series.py index b8ec00fb4d302..e73a69af8a799 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -922,7 +922,7 @@ def _get_with(self, key): "Indexing a Series with DataFrame is not " "supported, use the appropriate DataFrame column" ) - elif isinstance(key, tuple) and isinstance(self.index, MultiIndex): + elif isinstance(key, tuple): return self._get_values_tuple(key) elif not is_list_like(key): @@ -958,7 +958,7 @@ def _get_values_tuple(self, key): return result if not isinstance(self.index, MultiIndex): - raise ValueError("Can only tuple-index with a MultiIndex") + raise ValueError("key of type tuple not found and not a MultiIndex") # If key is contained, would have returned by now indexer, new_index = self.index.get_loc_level(key) @@ -1014,7 +1014,9 @@ def __setitem__(self, key, value): except TypeError as e: if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): - raise ValueError("Can only tuple-index with a MultiIndex") from e + raise ValueError( + "key of type tuple not found and not a MultiIndex" + ) from e if com.is_bool_indexer(key): key = check_bool_indexer(self.index, key) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index a7e72ec423dcb..1fafdf00393e1 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -383,7 +383,7 @@ def test_2d_to_1d_assignment_raises(): @pytest.mark.filterwarnings("ignore:Using a non-tuple:FutureWarning") def test_basic_getitem_setitem_corner(datetime_series): # invalid tuples, e.g. td.ts[:, None] vs. td.ts[:, 2] - msg = "Can only tuple-index with a MultiIndex" + msg = "key of type tuple not found and not a MultiIndex" with pytest.raises(ValueError, match=msg): datetime_series[:, 2] with pytest.raises(ValueError, match=msg): From bed29b31873a6ff94d890b1e4c4fd32b86fb8306 Mon Sep 17 00:00:00 2001 From: rhshadrach Date: Wed, 9 Sep 2020 16:55:21 -0400 Subject: [PATCH 08/10] Moved whatsnew to 1.1.3 --- doc/source/whatsnew/v1.1.2.rst | 2 -- doc/source/whatsnew/v1.1.3.rst | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.1.2.rst b/doc/source/whatsnew/v1.1.2.rst index f1d91e525ac11..81b8e7df11625 100644 --- a/doc/source/whatsnew/v1.1.2.rst +++ b/doc/source/whatsnew/v1.1.2.rst @@ -23,8 +23,6 @@ Fixed regressions - Regression in :meth:`DataFrame.replace` where a ``TypeError`` would be raised when attempting to replace elements of type :class:`Interval` (:issue:`35931`) - Fix regression in pickle roundtrip of the ``closed`` attribute of :class:`IntervalIndex` (:issue:`35658`) - Fixed regression in :meth:`DataFrameGroupBy.agg` where a ``ValueError: buffer source array is read-only`` would be raised when the underlying array is read-only (:issue:`36014`) -- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple. (:issue:`35534`) -- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset. (:issue:`35747`) - Fixed regression in :meth:`Series.groupby.rolling` number of levels of :class:`MultiIndex` in input was compressed to one (:issue:`36018`) - Fixed regression in :class:`DataFrameGroupBy` on an empty :class:`DataFrame` (:issue:`36197`) diff --git a/doc/source/whatsnew/v1.1.3.rst b/doc/source/whatsnew/v1.1.3.rst index e3161012da5d1..23db827218fbb 100644 --- a/doc/source/whatsnew/v1.1.3.rst +++ b/doc/source/whatsnew/v1.1.3.rst @@ -14,6 +14,8 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple. (:issue:`35534`) +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset. (:issue:`35747`) - .. --------------------------------------------------------------------------- From a4f90b183dd10fc6d7f7abf24e5feb42a316ffce Mon Sep 17 00:00:00 2001 From: rhshadrach Date: Wed, 9 Sep 2020 17:46:26 -0400 Subject: [PATCH 09/10] Removed periods from whatsnew --- doc/source/whatsnew/v1.1.3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.1.3.rst b/doc/source/whatsnew/v1.1.3.rst index 23db827218fbb..5aca33869bca1 100644 --- a/doc/source/whatsnew/v1.1.3.rst +++ b/doc/source/whatsnew/v1.1.3.rst @@ -14,8 +14,8 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ -- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple. (:issue:`35534`) -- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset. (:issue:`35747`) +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple (:issue:`35534`) +- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset (:issue:`35747`) - .. --------------------------------------------------------------------------- From d6ffb46a915341c7c8f3b5586943f01584880601 Mon Sep 17 00:00:00 2001 From: rhshadrach Date: Sat, 12 Sep 2020 00:10:49 -0400 Subject: [PATCH 10/10] Comment and rename --- pandas/core/series.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 98338396aec08..162604c0544a5 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -890,6 +890,7 @@ def __getitem__(self, key): if is_hashable(key): # Otherwise index.get_value will raise InvalidIndexError try: + # For labels that don't resolve as scalars like tuples and frozensets result = self._get_value(key) return result @@ -1012,11 +1013,11 @@ def __setitem__(self, key, value): # GH#12862 adding an new key to the Series self.loc[key] = value - except TypeError as e: + except TypeError as err: if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): raise ValueError( "key of type tuple not found and not a MultiIndex" - ) from e + ) from err if com.is_bool_indexer(key): key = check_bool_indexer(self.index, key)