|
9 | 9 | is_categorical_dtype,
|
10 | 10 | is_list_like,
|
11 | 11 | is_sequence,
|
| 12 | + is_iterator, |
12 | 13 | is_scalar,
|
13 | 14 | is_sparse,
|
14 | 15 | _is_unorderable_exception,
|
@@ -1300,17 +1301,24 @@ class _LocationIndexer(_NDFrameIndexer):
|
1300 | 1301 | _exception = Exception
|
1301 | 1302 |
|
1302 | 1303 | def __getitem__(self, key):
|
1303 |
| - if isinstance(key, tuple): |
1304 |
| - key = tuple(com._apply_if_callable(x, self.obj) for x in key) |
1305 |
| - else: |
1306 |
| - # scalar callable may return tuple |
1307 |
| - key = com._apply_if_callable(key, self.obj) |
1308 |
| - |
1309 | 1304 | if type(key) is tuple:
|
| 1305 | + key = tuple(com._apply_if_callable(x, self.obj) for x in key) |
| 1306 | + try: |
| 1307 | + if self._is_scalar_access(key): |
| 1308 | + return self._getitem_scalar(key) |
| 1309 | + except (KeyError, IndexError): |
| 1310 | + pass |
1310 | 1311 | return self._getitem_tuple(key)
|
1311 | 1312 | else:
|
| 1313 | + key = com._apply_if_callable(key, self.obj) |
1312 | 1314 | return self._getitem_axis(key, axis=0)
|
1313 | 1315 |
|
| 1316 | + def _is_scalar_access(self, key): |
| 1317 | + raise NotImplementedError() |
| 1318 | + |
| 1319 | + def _getitem_scalar(self, key): |
| 1320 | + raise NotImplementedError() |
| 1321 | + |
1314 | 1322 | def _getitem_axis(self, key, axis=0):
|
1315 | 1323 | raise NotImplementedError()
|
1316 | 1324 |
|
@@ -1389,7 +1397,8 @@ def _has_valid_type(self, key, axis):
|
1389 | 1397 | return True
|
1390 | 1398 |
|
1391 | 1399 | # TODO: don't check the entire key unless necessary
|
1392 |
| - if len(key) and np.all(ax.get_indexer_for(key) < 0): |
| 1400 | + if (not is_iterator(key) and len(key) and |
| 1401 | + np.all(ax.get_indexer_for(key) < 0)): |
1393 | 1402 |
|
1394 | 1403 | raise KeyError("None of [%s] are in the [%s]" %
|
1395 | 1404 | (key, self.obj._get_axis_name(axis)))
|
@@ -1420,6 +1429,36 @@ def error():
|
1420 | 1429 |
|
1421 | 1430 | return True
|
1422 | 1431 |
|
| 1432 | + def _is_scalar_access(self, key): |
| 1433 | + # this is a shortcut accessor to both .loc and .iloc |
| 1434 | + # that provide the equivalent access of .at and .iat |
| 1435 | + # a) avoid getting things via sections and (to minimize dtype changes) |
| 1436 | + # b) provide a performant path |
| 1437 | + if not hasattr(key, '__len__'): |
| 1438 | + return False |
| 1439 | + |
| 1440 | + if len(key) != self.ndim: |
| 1441 | + return False |
| 1442 | + |
| 1443 | + for i, k in enumerate(key): |
| 1444 | + if not is_scalar(k): |
| 1445 | + return False |
| 1446 | + |
| 1447 | + ax = self.obj.axes[i] |
| 1448 | + if isinstance(ax, MultiIndex): |
| 1449 | + return False |
| 1450 | + |
| 1451 | + if not ax.is_unique: |
| 1452 | + return False |
| 1453 | + |
| 1454 | + return True |
| 1455 | + |
| 1456 | + def _getitem_scalar(self, key): |
| 1457 | + # a fast-path to scalar access |
| 1458 | + # if not, raise |
| 1459 | + values = self.obj.get_value(*key) |
| 1460 | + return values |
| 1461 | + |
1423 | 1462 | def _get_partial_string_timestamp_match_key(self, key, labels):
|
1424 | 1463 | """Translate any partial string timestamp matches in key, returning the
|
1425 | 1464 | new key (GH 10331)"""
|
@@ -1536,6 +1575,33 @@ def _has_valid_type(self, key, axis):
|
1536 | 1575 | def _has_valid_setitem_indexer(self, indexer):
|
1537 | 1576 | self._has_valid_positional_setitem_indexer(indexer)
|
1538 | 1577 |
|
| 1578 | + def _is_scalar_access(self, key): |
| 1579 | + # this is a shortcut accessor to both .loc and .iloc |
| 1580 | + # that provide the equivalent access of .at and .iat |
| 1581 | + # a) avoid getting things via sections and (to minimize dtype changes) |
| 1582 | + # b) provide a performant path |
| 1583 | + if not hasattr(key, '__len__'): |
| 1584 | + return False |
| 1585 | + |
| 1586 | + if len(key) != self.ndim: |
| 1587 | + return False |
| 1588 | + |
| 1589 | + for i, k in enumerate(key): |
| 1590 | + if not is_integer(k): |
| 1591 | + return False |
| 1592 | + |
| 1593 | + ax = self.obj.axes[i] |
| 1594 | + if not ax.is_unique: |
| 1595 | + return False |
| 1596 | + |
| 1597 | + return True |
| 1598 | + |
| 1599 | + def _getitem_scalar(self, key): |
| 1600 | + # a fast-path to scalar access |
| 1601 | + # if not, raise |
| 1602 | + values = self.obj.get_value(*key, takeable=True) |
| 1603 | + return values |
| 1604 | + |
1539 | 1605 | def _is_valid_integer(self, key, axis):
|
1540 | 1606 | # return a boolean if we have a valid integer indexer
|
1541 | 1607 |
|
|
0 commit comments