Skip to content

Commit 2d4b1c3

Browse files
toobazvictor
authored and
victor
committed
PERF: do not check for label presence preventively (pandas-dev#21594)
closes pandas-dev#21593
1 parent e3658c7 commit 2d4b1c3

File tree

4 files changed

+20
-36
lines changed

4 files changed

+20
-36
lines changed

doc/source/whatsnew/v0.24.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Other Enhancements
1717
- :func:`to_datetime` now supports the ``%Z`` and ``%z`` directive when passed into ``format`` (:issue:`13486`)
1818
- :func:`Series.mode` and :func:`DataFrame.mode` now support the ``dropna`` parameter which can be used to specify whether NaN/NaT values should be considered (:issue:`17534`)
1919
- :func:`to_csv` now supports ``compression`` keyword when a file handle is passed. (:issue:`21227`)
20-
- :meth:`Index.droplevel` is now implemented also for flat indexes, for compatibility with MultiIndex (:issue:`21115`)
20+
- :meth:`Index.droplevel` is now implemented also for flat indexes, for compatibility with :class:`MultiIndex` (:issue:`21115`)
2121

2222

2323
.. _whatsnew_0240.api_breaking:
@@ -199,6 +199,7 @@ Indexing
199199
^^^^^^^^
200200

201201
- The traceback from a ``KeyError`` when asking ``.loc`` for a single missing label is now shorter and more clear (:issue:`21557`)
202+
- When ``.ix`` is asked for a missing integer label in a :class:`MultiIndex` with a first level of integer type, it now raises a ``KeyError`` - consistently with the case of a flat :class:`Int64Index` - rather than falling back to positional indexing (:issue:`21593`)
202203
-
203204
-
204205

pandas/core/indexing.py

+5-31
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
is_iterator,
1414
is_scalar,
1515
is_sparse,
16-
_is_unorderable_exception,
1716
_ensure_platform_int)
1817
from pandas.core.dtypes.missing import isna, _infer_fill_value
1918
from pandas.errors import AbstractMethodError
@@ -139,10 +138,7 @@ def _get_label(self, label, axis=None):
139138
# as its basically direct indexing
140139
# but will fail when the index is not present
141140
# see GH5667
142-
try:
143-
return self.obj._xs(label, axis=axis)
144-
except:
145-
return self.obj[label]
141+
return self.obj._xs(label, axis=axis)
146142
elif isinstance(label, tuple) and isinstance(label[axis], slice):
147143
raise IndexingError('no slices here, handle elsewhere')
148144

@@ -1797,42 +1793,20 @@ class _LocIndexer(_LocationIndexer):
17971793

17981794
@Appender(_NDFrameIndexer._validate_key.__doc__)
17991795
def _validate_key(self, key, axis):
1800-
ax = self.obj._get_axis(axis)
18011796

1802-
# valid for a label where all labels are in the index
1797+
# valid for a collection of labels (we check their presence later)
18031798
# slice of labels (where start-end in labels)
18041799
# slice of integers (only if in the labels)
18051800
# boolean
18061801

18071802
if isinstance(key, slice):
18081803
return
18091804

1810-
elif com.is_bool_indexer(key):
1805+
if com.is_bool_indexer(key):
18111806
return
18121807

1813-
elif not is_list_like_indexer(key):
1814-
1815-
def error():
1816-
if isna(key):
1817-
raise TypeError("cannot use label indexing with a null "
1818-
"key")
1819-
raise KeyError(u"the label [{key}] is not in the [{axis}]"
1820-
.format(key=key,
1821-
axis=self.obj._get_axis_name(axis)))
1822-
1823-
try:
1824-
key = self._convert_scalar_indexer(key, axis)
1825-
except TypeError as e:
1826-
1827-
# python 3 type errors should be raised
1828-
if _is_unorderable_exception(e):
1829-
error()
1830-
raise
1831-
except:
1832-
error()
1833-
1834-
if not ax.contains(key):
1835-
error()
1808+
if not is_list_like_indexer(key):
1809+
self._convert_scalar_indexer(key, axis)
18361810

18371811
def _is_scalar_access(self, key):
18381812
# this is a shortcut accessor to both .loc and .iloc

pandas/tests/indexes/datetimes/test_partial_slicing.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
date_range, Index, Timedelta, Timestamp)
1212
from pandas.util import testing as tm
1313

14+
from pandas.core.indexing import IndexingError
15+
1416

1517
class TestSlicing(object):
1618
def test_dti_slicing(self):
@@ -313,12 +315,12 @@ def test_partial_slicing_with_multiindex(self):
313315
result = df_multi.loc[('2013-06-19 09:30:00', 'ACCT1', 'ABC')]
314316
tm.assert_series_equal(result, expected)
315317

316-
# this is a KeyError as we don't do partial string selection on
317-
# multi-levels
318+
# this is an IndexingError as we don't do partial string selection on
319+
# multi-levels.
318320
def f():
319321
df_multi.loc[('2013-06-19', 'ACCT1', 'ABC')]
320322

321-
pytest.raises(KeyError, f)
323+
pytest.raises(IndexingError, f)
322324

323325
# GH 4294
324326
# partial slice on a series mi

pandas/tests/indexing/test_multiindex.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ def test_iloc_getitem_multiindex(self):
230230
# corner column
231231
rs = mi_int.iloc[2, 2]
232232
with catch_warnings(record=True):
233-
xp = mi_int.ix[:, 2].ix[2]
233+
# First level is int - so use .loc rather than .ix (GH 21593)
234+
xp = mi_int.loc[(8, 12), (4, 10)]
234235
assert rs == xp
235236

236237
# this is basically regular indexing
@@ -278,6 +279,12 @@ def test_loc_multiindex(self):
278279
xp = mi_int.ix[4]
279280
tm.assert_frame_equal(rs, xp)
280281

282+
# missing label
283+
pytest.raises(KeyError, lambda: mi_int.loc[2])
284+
with catch_warnings(record=True):
285+
# GH 21593
286+
pytest.raises(KeyError, lambda: mi_int.ix[2])
287+
281288
def test_getitem_partial_int(self):
282289
# GH 12416
283290
# with single item

0 commit comments

Comments
 (0)