|
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,
|
@@ -859,15 +860,20 @@ def _convert_for_reindex(self, key, axis=0):
|
859 | 860 | return labels[key]
|
860 | 861 | else:
|
861 | 862 | if isinstance(key, Index):
|
862 |
| - # want Index objects to pass through untouched |
863 |
| - keyarr = key |
| 863 | + keyarr = labels._convert_index_indexer(key) |
864 | 864 | else:
|
865 | 865 | # asarray can be unsafe, NumPy strings are weird
|
866 | 866 | keyarr = _asarray_tuplesafe(key)
|
867 | 867 |
|
868 |
| - if is_integer_dtype(keyarr) and not labels.is_integer(): |
869 |
| - keyarr = _ensure_platform_int(keyarr) |
870 |
| - return labels.take(keyarr) |
| 868 | + if is_integer_dtype(keyarr): |
| 869 | + # Cast the indexer to uint64 if possible so |
| 870 | + # that the values returned from indexing are |
| 871 | + # also uint64. |
| 872 | + keyarr = labels._convert_arr_indexer(keyarr) |
| 873 | + |
| 874 | + if not labels.is_integer(): |
| 875 | + keyarr = _ensure_platform_int(keyarr) |
| 876 | + return labels.take(keyarr) |
871 | 877 |
|
872 | 878 | return keyarr
|
873 | 879 |
|
@@ -1043,11 +1049,10 @@ def _getitem_iterable(self, key, axis=0):
|
1043 | 1049 | return self.obj.take(inds, axis=axis, convert=False)
|
1044 | 1050 | else:
|
1045 | 1051 | if isinstance(key, Index):
|
1046 |
| - # want Index objects to pass through untouched |
1047 |
| - keyarr = key |
| 1052 | + keyarr = labels._convert_index_indexer(key) |
1048 | 1053 | else:
|
1049 |
| - # asarray can be unsafe, NumPy strings are weird |
1050 | 1054 | keyarr = _asarray_tuplesafe(key)
|
| 1055 | + keyarr = labels._convert_arr_indexer(keyarr) |
1051 | 1056 |
|
1052 | 1057 | if is_categorical_dtype(labels):
|
1053 | 1058 | keyarr = labels._shallow_copy(keyarr)
|
@@ -1300,17 +1305,24 @@ class _LocationIndexer(_NDFrameIndexer):
|
1300 | 1305 | _exception = Exception
|
1301 | 1306 |
|
1302 | 1307 | 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 | 1308 | if type(key) is tuple:
|
| 1309 | + key = tuple(com._apply_if_callable(x, self.obj) for x in key) |
| 1310 | + try: |
| 1311 | + if self._is_scalar_access(key): |
| 1312 | + return self._getitem_scalar(key) |
| 1313 | + except (KeyError, IndexError): |
| 1314 | + pass |
1310 | 1315 | return self._getitem_tuple(key)
|
1311 | 1316 | else:
|
| 1317 | + key = com._apply_if_callable(key, self.obj) |
1312 | 1318 | return self._getitem_axis(key, axis=0)
|
1313 | 1319 |
|
| 1320 | + def _is_scalar_access(self, key): |
| 1321 | + raise NotImplementedError() |
| 1322 | + |
| 1323 | + def _getitem_scalar(self, key): |
| 1324 | + raise NotImplementedError() |
| 1325 | + |
1314 | 1326 | def _getitem_axis(self, key, axis=0):
|
1315 | 1327 | raise NotImplementedError()
|
1316 | 1328 |
|
@@ -1389,7 +1401,8 @@ def _has_valid_type(self, key, axis):
|
1389 | 1401 | return True
|
1390 | 1402 |
|
1391 | 1403 | # TODO: don't check the entire key unless necessary
|
1392 |
| - if len(key) and np.all(ax.get_indexer_for(key) < 0): |
| 1404 | + if (not is_iterator(key) and len(key) and |
| 1405 | + np.all(ax.get_indexer_for(key) < 0)): |
1393 | 1406 |
|
1394 | 1407 | raise KeyError("None of [%s] are in the [%s]" %
|
1395 | 1408 | (key, self.obj._get_axis_name(axis)))
|
@@ -1420,6 +1433,36 @@ def error():
|
1420 | 1433 |
|
1421 | 1434 | return True
|
1422 | 1435 |
|
| 1436 | + def _is_scalar_access(self, key): |
| 1437 | + # this is a shortcut accessor to both .loc and .iloc |
| 1438 | + # that provide the equivalent access of .at and .iat |
| 1439 | + # a) avoid getting things via sections and (to minimize dtype changes) |
| 1440 | + # b) provide a performant path |
| 1441 | + if not hasattr(key, '__len__'): |
| 1442 | + return False |
| 1443 | + |
| 1444 | + if len(key) != self.ndim: |
| 1445 | + return False |
| 1446 | + |
| 1447 | + for i, k in enumerate(key): |
| 1448 | + if not is_scalar(k): |
| 1449 | + return False |
| 1450 | + |
| 1451 | + ax = self.obj.axes[i] |
| 1452 | + if isinstance(ax, MultiIndex): |
| 1453 | + return False |
| 1454 | + |
| 1455 | + if not ax.is_unique: |
| 1456 | + return False |
| 1457 | + |
| 1458 | + return True |
| 1459 | + |
| 1460 | + def _getitem_scalar(self, key): |
| 1461 | + # a fast-path to scalar access |
| 1462 | + # if not, raise |
| 1463 | + values = self.obj.get_value(*key) |
| 1464 | + return values |
| 1465 | + |
1423 | 1466 | def _get_partial_string_timestamp_match_key(self, key, labels):
|
1424 | 1467 | """Translate any partial string timestamp matches in key, returning the
|
1425 | 1468 | new key (GH 10331)"""
|
@@ -1536,6 +1579,33 @@ def _has_valid_type(self, key, axis):
|
1536 | 1579 | def _has_valid_setitem_indexer(self, indexer):
|
1537 | 1580 | self._has_valid_positional_setitem_indexer(indexer)
|
1538 | 1581 |
|
| 1582 | + def _is_scalar_access(self, key): |
| 1583 | + # this is a shortcut accessor to both .loc and .iloc |
| 1584 | + # that provide the equivalent access of .at and .iat |
| 1585 | + # a) avoid getting things via sections and (to minimize dtype changes) |
| 1586 | + # b) provide a performant path |
| 1587 | + if not hasattr(key, '__len__'): |
| 1588 | + return False |
| 1589 | + |
| 1590 | + if len(key) != self.ndim: |
| 1591 | + return False |
| 1592 | + |
| 1593 | + for i, k in enumerate(key): |
| 1594 | + if not is_integer(k): |
| 1595 | + return False |
| 1596 | + |
| 1597 | + ax = self.obj.axes[i] |
| 1598 | + if not ax.is_unique: |
| 1599 | + return False |
| 1600 | + |
| 1601 | + return True |
| 1602 | + |
| 1603 | + def _getitem_scalar(self, key): |
| 1604 | + # a fast-path to scalar access |
| 1605 | + # if not, raise |
| 1606 | + values = self.obj.get_value(*key, takeable=True) |
| 1607 | + return values |
| 1608 | + |
1539 | 1609 | def _is_valid_integer(self, key, axis):
|
1540 | 1610 | # return a boolean if we have a valid integer indexer
|
1541 | 1611 |
|
|
0 commit comments