From c269e137b112044656c09e8ad27799e56fd3d18f Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Mon, 30 Apr 2018 12:41:06 -0400 Subject: [PATCH 1/7] BUG: Fix Series.get() for ExtensionArray and Categorical --- pandas/core/indexes/base.py | 22 ++++++++++++++++------ pandas/tests/extension/base/getitem.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 2ceec1592d49b..6f96b78a9dbc6 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3068,13 +3068,23 @@ def get_value(self, series, key): # if we have something that is Index-like, then # use this, e.g. DatetimeIndex s = getattr(series, '_values', None) - if isinstance(s, (ExtensionArray, Index)) and is_scalar(key): - try: - return s[key] - except (IndexError, ValueError): + if is_scalar(key): + if isinstance(s, Index): + try: + return s[key] + except (IndexError, ValueError): - # invalid type as an indexer - pass + # invalid type as an indexer + pass + elif isinstance(s, ExtensionArray): + try: + # This should call the ExtensionArray __getitem__ + iloc = self.get_loc(key) + return s[iloc] + except (IndexError, ValueError): + + # invalid type as an indexer + pass s = com._values_from_object(series) k = com._values_from_object(key) diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py index 5c9ede1079079..238ab81e009df 100644 --- a/pandas/tests/extension/base/getitem.py +++ b/pandas/tests/extension/base/getitem.py @@ -117,6 +117,18 @@ def test_getitem_slice(self, data): result = data[slice(1)] # scalar assert isinstance(result, type(data)) + def test_get(self, data): + # GH 20882 + s = pd.Series(data, index=[2 * i for i in range(len(data))]) + assert s.get(4) == s.iloc[2] + + result = s.get([4, 6]) + expected = s.iloc[[2, 3]] + self.assert_series_equal(result, expected) + + s = pd.Series(data[:6], index=list('abcdef')) + assert s.get('c') == s.iloc[2] + def test_take_sequence(self, data): result = pd.Series(data)[[0, 1, 3]] assert result.iloc[0] == data[0] From b7f2a6f47615ba8d449ba10a3fae9a52c8553ea7 Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Mon, 30 Apr 2018 15:22:46 -0400 Subject: [PATCH 2/7] Add additional tests --- pandas/tests/extension/base/getitem.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py index 238ab81e009df..5c9f56be66b91 100644 --- a/pandas/tests/extension/base/getitem.py +++ b/pandas/tests/extension/base/getitem.py @@ -126,9 +126,20 @@ def test_get(self, data): expected = s.iloc[[2, 3]] self.assert_series_equal(result, expected) + result = s.get(slice(2)) + expected = s.iloc[[0, 1]] + self.assert_series_equal(result, expected) + s = pd.Series(data[:6], index=list('abcdef')) assert s.get('c') == s.iloc[2] + result = s.get(slice('b', 'd')) + expected = s.iloc[[1, 2, 3]] + self.assert_series_equal(result, expected) + + result = s.get('Z') + assert result is None + def test_take_sequence(self, data): result = pd.Series(data)[[0, 1, 3]] assert result.iloc[0] == data[0] From c04f77c78bdb2cc4d3430a045d88ac007bf7d7fc Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Fri, 4 May 2018 17:45:43 -0400 Subject: [PATCH 3/7] Additional test for .get with ExtensionArray --- pandas/core/indexes/base.py | 10 +++++----- pandas/tests/extension/base/getitem.py | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 8008144ae6021..663a2efbf2609 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3091,13 +3091,13 @@ def get_value(self, series, key): pass elif isinstance(s, ExtensionArray): try: - # This should call the ExtensionArray __getitem__ iloc = self.get_loc(key) + # This should call the ExtensionArray __getitem__ return s[iloc] - except (IndexError, ValueError): - - # invalid type as an indexer - pass + except KeyError: + if isinstance(key, (int, np.integer)): + # This should call the ExtensionArray __getitem__ + return s[key] s = com._values_from_object(series) k = com._values_from_object(key) diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py index 5c9f56be66b91..0f480e9c65ff6 100644 --- a/pandas/tests/extension/base/getitem.py +++ b/pandas/tests/extension/base/getitem.py @@ -140,6 +140,8 @@ def test_get(self, data): result = s.get('Z') assert result is None + assert s.get(4) == s.iloc[4] + def test_take_sequence(self, data): result = pd.Series(data)[[0, 1, 3]] assert result.iloc[0] == data[0] From 1bbaa2b10629d18c1ac9797187591498e2a7655a Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Sat, 5 May 2018 05:24:18 -0400 Subject: [PATCH 4/7] Get Exception handling right --- pandas/core/indexes/base.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 663a2efbf2609..2795e6dadbb6d 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3082,21 +3082,12 @@ def get_value(self, series, key): # use this, e.g. DatetimeIndex s = getattr(series, '_values', None) if is_scalar(key): - if isinstance(s, Index): - try: - return s[key] - except (IndexError, ValueError): - - # invalid type as an indexer - pass - elif isinstance(s, ExtensionArray): + if isinstance(s, (Index, ExtensionArray)): try: iloc = self.get_loc(key) - # This should call the ExtensionArray __getitem__ return s[iloc] except KeyError: if isinstance(key, (int, np.integer)): - # This should call the ExtensionArray __getitem__ return s[key] s = com._values_from_object(series) From 0a88cdd28b0fb668a6b9dbf482f0dadb0ff5a31d Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Sat, 5 May 2018 09:31:13 -0400 Subject: [PATCH 5/7] Use isinteger and add comments --- pandas/core/indexes/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 2795e6dadbb6d..8a6fb70e0b6d4 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3083,11 +3083,16 @@ def get_value(self, series, key): s = getattr(series, '_values', None) if is_scalar(key): if isinstance(s, (Index, ExtensionArray)): + # GH 20825 + # Unify Index and ExtensionArray treatment + # First try to convert the key to a location + # If that fails, see if key is an integer, and + # try that try: iloc = self.get_loc(key) return s[iloc] except KeyError: - if isinstance(key, (int, np.integer)): + if is_integer(key): return s[key] s = com._values_from_object(series) From 894edc8f7488c9511c34418ef0dfe82b57863a02 Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Sat, 5 May 2018 20:02:02 -0400 Subject: [PATCH 6/7] add boundary tests --- pandas/core/indexes/base.py | 25 ++++++++++++------------- pandas/tests/extension/base/getitem.py | 5 +++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 8a6fb70e0b6d4..22b9b73fb5ca6 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3081,19 +3081,18 @@ def get_value(self, series, key): # if we have something that is Index-like, then # use this, e.g. DatetimeIndex s = getattr(series, '_values', None) - if is_scalar(key): - if isinstance(s, (Index, ExtensionArray)): - # GH 20825 - # Unify Index and ExtensionArray treatment - # First try to convert the key to a location - # If that fails, see if key is an integer, and - # try that - try: - iloc = self.get_loc(key) - return s[iloc] - except KeyError: - if is_integer(key): - return s[key] + if isinstance(s, (Index, ExtensionArray)) and is_scalar(key): + # GH 20825 + # Unify Index and ExtensionArray treatment + # First try to convert the key to a location + # If that fails, see if key is an integer, and + # try that + try: + iloc = self.get_loc(key) + return s[iloc] + except KeyError: + if is_integer(key): + return s[key] s = com._values_from_object(series) k = com._values_from_object(key) diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py index 0f480e9c65ff6..883b3f5588aef 100644 --- a/pandas/tests/extension/base/getitem.py +++ b/pandas/tests/extension/base/getitem.py @@ -130,6 +130,9 @@ def test_get(self, data): expected = s.iloc[[0, 1]] self.assert_series_equal(result, expected) + assert s.get(-1) == s.iloc[-1] + assert s.get(s.index.max() + 1) is None + s = pd.Series(data[:6], index=list('abcdef')) assert s.get('c') == s.iloc[2] @@ -141,6 +144,8 @@ def test_get(self, data): assert result is None assert s.get(4) == s.iloc[4] + assert s.get(-1) == s.iloc[-1] + assert s.get(len(s)) is None def test_take_sequence(self, data): result = pd.Series(data)[[0, 1, 3]] From 65087a6de08b35ccc696051f508275747deabaf6 Mon Sep 17 00:00:00 2001 From: Dr-Irv Date: Sat, 5 May 2018 20:03:46 -0400 Subject: [PATCH 7/7] fix if test --- pandas/core/indexes/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 22b9b73fb5ca6..7837faf5b4c0f 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3081,7 +3081,7 @@ def get_value(self, series, key): # if we have something that is Index-like, then # use this, e.g. DatetimeIndex s = getattr(series, '_values', None) - if isinstance(s, (Index, ExtensionArray)) and is_scalar(key): + if isinstance(s, (ExtensionArray, Index)) and is_scalar(key): # GH 20825 # Unify Index and ExtensionArray treatment # First try to convert the key to a location