Skip to content

Commit 1ab7b7c

Browse files
committed
PERF: do not try to lookup value before actually looking it up
closes pandas-dev#21593
1 parent f1ffc5f commit 1ab7b7c

File tree

4 files changed

+18
-35
lines changed

4 files changed

+18
-35
lines changed

doc/source/whatsnew/v0.24.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Performance Improvements
130130

131131
- Improved performance of :func:`Series.describe` in case of numeric dtpyes (:issue:`21274`)
132132
- Improved performance of :func:`pandas.core.groupby.GroupBy.rank` when dealing with tied rankings (:issue:`21237`)
133+
- Improved performance of label indexing when key is a single label (:issue:`21594`)
133134
-
134135

135136
.. _whatsnew_0240.docs:
@@ -198,6 +199,7 @@ Indexing
198199
^^^^^^^^
199200

200201
- 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`` (:issue:`21593`)
201203
-
202204
-
203205

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

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

14+
from pandas.core.indexing import IndexingError
1415

1516
class TestSlicing(object):
1617
def test_dti_slicing(self):
@@ -313,12 +314,12 @@ def test_partial_slicing_with_multiindex(self):
313314
result = df_multi.loc[('2013-06-19 09:30:00', 'ACCT1', 'ABC')]
314315
tm.assert_series_equal(result, expected)
315316

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

321-
pytest.raises(KeyError, f)
322+
pytest.raises(IndexingError, f)
322323

323324
# GH 4294
324325
# partial slice on a series mi

pandas/tests/indexing/test_multiindex.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ 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+
xp = mi_int.loc[(8, 12), (4, 10)]
234234
assert rs == xp
235235

236236
# this is basically regular indexing
@@ -278,6 +278,12 @@ def test_loc_multiindex(self):
278278
xp = mi_int.ix[4]
279279
tm.assert_frame_equal(rs, xp)
280280

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

0 commit comments

Comments
 (0)