Skip to content

Commit c7de259

Browse files
jbrockmendelroberthdevries
authored andcommitted
API/BUG: raise only KeyError failed on geitem/loc lookups (pandas-dev#31867)
1 parent 8576110 commit c7de259

File tree

8 files changed

+89
-33
lines changed

8 files changed

+89
-33
lines changed

doc/source/whatsnew/v1.1.0.rst

+70-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,76 @@ Backwards incompatible API changes
9090
now raise a ``TypeError`` if a not-accepted keyword argument is passed into it.
9191
Previously a ``UnsupportedFunctionCall`` was raised (``AssertionError`` if ``min_count`` passed into :meth:`~DataFrameGroupby.median``) (:issue:`31485`)
9292
- :meth:`DataFrame.at` and :meth:`Series.at` will raise a ``TypeError`` instead of a ``ValueError`` if an incompatible key is passed, and ``KeyError`` if a missing key is passed, matching the behavior of ``.loc[]`` (:issue:`31722`)
93-
-
93+
94+
.. _whatsnew_110.api_breaking.indexing_raises_key_errors:
95+
96+
Failed Label-Based Lookups Always Raise KeyError
97+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
98+
99+
Label lookups ``series[key]``, ``series.loc[key]`` and ``frame.loc[key]``
100+
used to raises either ``KeyError`` or ``TypeError`` depending on the type of
101+
key and type of :class:`Index`. These now consistently raise ``KeyError`` (:issue:`31867`)
102+
103+
.. ipython:: python
104+
105+
ser1 = pd.Series(range(3), index=[0, 1, 2])
106+
ser2 = pd.Series(range(3), index=pd.date_range("2020-02-01", periods=3))
107+
108+
*Previous behavior*:
109+
110+
.. code-block:: ipython
111+
112+
In [3]: ser1[1.5]
113+
...
114+
TypeError: cannot do label indexing on Int64Index with these indexers [1.5] of type float
115+
116+
In [4] ser1["foo"]
117+
...
118+
KeyError: 'foo'
119+
120+
In [5]: ser1.loc[1.5]
121+
...
122+
TypeError: cannot do label indexing on Int64Index with these indexers [1.5] of type float
123+
124+
In [6]: ser1.loc["foo"]
125+
...
126+
KeyError: 'foo'
127+
128+
In [7]: ser2.loc[1]
129+
...
130+
TypeError: cannot do label indexing on DatetimeIndex with these indexers [1] of type int
131+
132+
In [8]: ser2.loc[pd.Timestamp(0)]
133+
...
134+
KeyError: Timestamp('1970-01-01 00:00:00')
135+
136+
*New behavior*:
137+
138+
.. code-block:: ipython
139+
140+
In [3]: ser1[1.5]
141+
...
142+
KeyError: 1.5
143+
144+
In [4] ser1["foo"]
145+
...
146+
KeyError: 'foo'
147+
148+
In [5]: ser1.loc[1.5]
149+
...
150+
KeyError: 1.5
151+
152+
In [6]: ser1.loc["foo"]
153+
...
154+
KeyError: 'foo'
155+
156+
In [7]: ser2.loc[1]
157+
...
158+
KeyError: 1
159+
160+
In [8]: ser2.loc[pd.Timestamp(0)]
161+
...
162+
KeyError: Timestamp('1970-01-01 00:00:00')
94163
95164
.. ---------------------------------------------------------------------------
96165

pandas/core/indexes/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3096,7 +3096,7 @@ def _convert_scalar_indexer(self, key, kind: str_t):
30963096

30973097
if kind == "getitem" and is_float(key):
30983098
if not self.is_floating():
3099-
self._invalid_indexer("label", key)
3099+
raise KeyError(key)
31003100

31013101
elif kind == "loc" and is_float(key):
31023102

@@ -3110,11 +3110,11 @@ def _convert_scalar_indexer(self, key, kind: str_t):
31103110
"string",
31113111
"mixed",
31123112
]:
3113-
self._invalid_indexer("label", key)
3113+
raise KeyError(key)
31143114

31153115
elif kind == "loc" and is_integer(key):
31163116
if not (is_integer_dtype(self.dtype) or is_object_dtype(self.dtype)):
3117-
self._invalid_indexer("label", key)
3117+
raise KeyError(key)
31183118

31193119
return key
31203120

pandas/core/indexes/category.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ def _convert_scalar_indexer(self, key, kind: str):
581581
try:
582582
return self.categories._convert_scalar_indexer(key, kind="loc")
583583
except TypeError:
584-
self._invalid_indexer("label", key)
584+
raise KeyError(key)
585585
return super()._convert_scalar_indexer(key, kind=kind)
586586

587587
@Appender(Index._convert_list_indexer.__doc__)

pandas/core/indexes/datetimelike.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,9 @@ def _convert_scalar_indexer(self, key, kind: str):
397397
is_int = is_integer(key)
398398
is_flt = is_float(key)
399399
if kind == "loc" and (is_int or is_flt):
400-
self._invalid_indexer("label", key)
400+
raise KeyError(key)
401401
elif kind == "getitem" and is_flt:
402-
self._invalid_indexer("label", key)
402+
raise KeyError(key)
403403

404404
return super()._convert_scalar_indexer(key, kind=kind)
405405

pandas/core/indexing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,7 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False):
11431143
# try to find out correct indexer, if not type correct raise
11441144
try:
11451145
key = labels._convert_scalar_indexer(key, kind="loc")
1146-
except TypeError:
1146+
except KeyError:
11471147
# but we will allow setting
11481148
if not is_setter:
11491149
raise

pandas/tests/frame/test_constructors.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -1864,11 +1864,7 @@ def check(df):
18641864

18651865
# No NaN found -> error
18661866
if len(indexer) == 0:
1867-
msg = (
1868-
"cannot do label indexing on RangeIndex "
1869-
r"with these indexers \[nan\] of type float"
1870-
)
1871-
with pytest.raises(TypeError, match=msg):
1867+
with pytest.raises(KeyError, match="^nan$"):
18721868
df.loc[:, np.nan]
18731869
# single nan should result in Series
18741870
elif len(indexer) == 1:

pandas/tests/indexing/test_floats.py

+10-19
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@ def test_scalar_non_numeric(self, index_func, klass):
9797
# getting
9898
for idxr, getitem in [(lambda x: x.iloc, False), (lambda x: x, True)]:
9999

100-
# gettitem on a DataFrame is a KeyError as it is indexing
101-
# via labels on the columns
102-
if getitem and isinstance(s, DataFrame):
100+
if getitem:
103101
error = KeyError
104-
msg = r"^3(\.0)?$"
102+
msg = r"^3\.0?$"
105103
else:
106104
error = TypeError
107105
msg = (
@@ -120,6 +118,9 @@ def test_scalar_non_numeric(self, index_func, klass):
120118
"string",
121119
"unicode",
122120
"mixed",
121+
"period",
122+
"timedelta64",
123+
"datetime64",
123124
}:
124125
error = KeyError
125126
msg = r"^3\.0$"
@@ -181,12 +182,7 @@ def test_scalar_non_numeric_series_fallback(self, index_func):
181182
i = index_func(5)
182183
s = Series(np.arange(len(i)), index=i)
183184
s[3]
184-
msg = (
185-
r"cannot do (label|positional) indexing "
186-
fr"on {type(i).__name__} with these indexers \[3\.0\] of "
187-
"type float"
188-
)
189-
with pytest.raises(TypeError, match=msg):
185+
with pytest.raises(KeyError, match="^3.0$"):
190186
s[3.0]
191187

192188
def test_scalar_with_mixed(self):
@@ -197,12 +193,12 @@ def test_scalar_with_mixed(self):
197193
# lookup in a pure stringstr
198194
# with an invalid indexer
199195
msg = (
200-
"cannot do label indexing "
201-
fr"on {Index.__name__} with these indexers \[1\.0\] of "
196+
r"cannot do label indexing "
197+
r"on Index with these indexers \[1\.0\] of "
202198
r"type float|"
203199
"Cannot index by location index with a non-integer key"
204200
)
205-
with pytest.raises(TypeError, match=msg):
201+
with pytest.raises(KeyError, match="^1.0$"):
206202
s2[1.0]
207203
with pytest.raises(TypeError, match=msg):
208204
s2.iloc[1.0]
@@ -216,12 +212,7 @@ def test_scalar_with_mixed(self):
216212

217213
# mixed index so we have label
218214
# indexing
219-
msg = (
220-
"cannot do label indexing "
221-
fr"on {Index.__name__} with these indexers \[1\.0\] of "
222-
"type float"
223-
)
224-
with pytest.raises(TypeError, match=msg):
215+
with pytest.raises(KeyError, match="^1.0$"):
225216
s3[1.0]
226217

227218
result = s3[1]

pandas/tests/indexing/test_loc.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def test_loc_getitem_label_out_of_range(self):
3535
"loc", 20, typs=["ints", "uints", "mixed"], fails=KeyError,
3636
)
3737
self.check_result("loc", 20, typs=["labels"], fails=KeyError)
38-
self.check_result("loc", 20, typs=["ts"], axes=0, fails=TypeError)
38+
self.check_result("loc", 20, typs=["ts"], axes=0, fails=KeyError)
3939
self.check_result("loc", 20, typs=["floats"], axes=0, fails=KeyError)
4040

4141
def test_loc_getitem_label_list(self):

0 commit comments

Comments
 (0)