Skip to content

REF: define _convert_to_indexer in Loc #31585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 2, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 81 additions & 84 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ def iat(self) -> "_iAtIndexer":
return _iAtIndexer("iat", self)


class _NDFrameIndexer(_NDFrameIndexerBase):
class _LocationIndexer(_NDFrameIndexerBase):
_valid_types: str
axis = None

Expand Down Expand Up @@ -1580,10 +1580,6 @@ def _validate_read_indexer(
def _convert_to_indexer(self, key, axis: int):
raise AbstractMethodError(self)


class _LocationIndexer(_NDFrameIndexer):
_takeable: bool = False

def __getitem__(self, key):
if type(key) is tuple:
key = tuple(com.apply_if_callable(x, self.obj) for x in key)
Expand Down Expand Up @@ -1614,94 +1610,17 @@ def _getbool_axis(self, key, axis: int):
inds = key.nonzero()[0]
return self.obj._take_with_is_copy(inds, axis=axis)

def _convert_to_indexer(self, key, axis: int):
"""
Convert indexing key into something we can use to do actual fancy
indexing on a ndarray.

Examples
ix[:5] -> slice(0, 5)
ix[[1,2,3]] -> [1,2,3]
ix[['foo', 'bar', 'baz']] -> [i, j, k] (indices of foo, bar, baz)

Going by Zen of Python?
'In the face of ambiguity, refuse the temptation to guess.'
raise AmbiguousIndexError with integer labels?
- No, prefer label-based indexing
"""
labels = self.obj._get_axis(axis)

if isinstance(key, slice):
return self._convert_slice_indexer(key, axis)

# try to find out correct indexer, if not type correct raise
try:
key = self._convert_scalar_indexer(key, axis)
except TypeError:
# but we will allow setting
pass

# see if we are positional in nature
is_int_index = labels.is_integer()
is_int_positional = is_integer(key) and not is_int_index

if is_scalar(key) or isinstance(labels, ABCMultiIndex):
# Otherwise get_loc will raise InvalidIndexError

# if we are a label return me
try:
return labels.get_loc(key)
except LookupError:
if isinstance(key, tuple) and isinstance(labels, ABCMultiIndex):
if len(key) == labels.nlevels:
return {"key": key}
raise
except TypeError:
pass
except ValueError:
if not is_int_positional:
raise

# a positional
if is_int_positional:

# if we are setting and its not a valid location
# its an insert which fails by definition

# always valid
return {"key": key}

if is_nested_tuple(key, labels):
return labels.get_locs(key)

elif is_list_like_indexer(key):

if com.is_bool_indexer(key):
key = check_bool_indexer(labels, key)
(inds,) = key.nonzero()
return inds
else:
# When setting, missing keys are not allowed, even with .loc:
return self._get_listlike_indexer(key, axis, raise_missing=True)[1]
else:
try:
return labels.get_loc(key)
except LookupError:
# allow a not found key only if we are a setter
if not is_list_like_indexer(key):
return {"key": key}
raise


@Appender(IndexingMixin.loc.__doc__)
class _LocIndexer(_LocationIndexer):
_takeable: bool = False
_valid_types = (
"labels (MUST BE IN THE INDEX), slices of labels (BOTH "
"endpoints included! Can be slices of integers if the "
"index is integers), listlike of labels, boolean"
)

@Appender(_NDFrameIndexer._validate_key.__doc__)
@Appender(_LocationIndexer._validate_key.__doc__)
def _validate_key(self, key, axis: int):

# valid for a collection of labels (we check their presence later)
Expand Down Expand Up @@ -1867,6 +1786,84 @@ def _get_slice_axis(self, slice_obj: slice, axis: int):
# return a DatetimeIndex instead of a slice object.
return self.obj.take(indexer, axis=axis)

def _convert_to_indexer(self, key, axis: int):
"""
Convert indexing key into something we can use to do actual fancy
indexing on a ndarray.

Examples
ix[:5] -> slice(0, 5)
ix[[1,2,3]] -> [1,2,3]
ix[['foo', 'bar', 'baz']] -> [i, j, k] (indices of foo, bar, baz)

Going by Zen of Python?
'In the face of ambiguity, refuse the temptation to guess.'
raise AmbiguousIndexError with integer labels?
- No, prefer label-based indexing
"""
labels = self.obj._get_axis(axis)

if isinstance(key, slice):
return self._convert_slice_indexer(key, axis)

# try to find out correct indexer, if not type correct raise
try:
key = self._convert_scalar_indexer(key, axis)
except TypeError:
# but we will allow setting
pass

# see if we are positional in nature
is_int_index = labels.is_integer()
is_int_positional = is_integer(key) and not is_int_index

if is_scalar(key) or isinstance(labels, ABCMultiIndex):
# Otherwise get_loc will raise InvalidIndexError

# if we are a label return me
try:
return labels.get_loc(key)
except LookupError:
if isinstance(key, tuple) and isinstance(labels, ABCMultiIndex):
if len(key) == labels.nlevels:
return {"key": key}
raise
except TypeError:
pass
except ValueError:
if not is_int_positional:
raise

# a positional
if is_int_positional:

# if we are setting and its not a valid location
# its an insert which fails by definition

# always valid
return {"key": key}

if is_nested_tuple(key, labels):
return labels.get_locs(key)

elif is_list_like_indexer(key):

if com.is_bool_indexer(key):
key = check_bool_indexer(labels, key)
(inds,) = key.nonzero()
return inds
else:
# When setting, missing keys are not allowed, even with .loc:
return self._get_listlike_indexer(key, axis, raise_missing=True)[1]
else:
try:
return labels.get_loc(key)
except LookupError:
# allow a not found key only if we are a setter
if not is_list_like_indexer(key):
return {"key": key}
raise


@Appender(IndexingMixin.iloc.__doc__)
class _iLocIndexer(_LocationIndexer):
Expand Down