From 6d25f00259cedd1666a7a394fdd32f3972d43472 Mon Sep 17 00:00:00 2001 From: tp Date: Tue, 26 Sep 2017 00:25:37 +0200 Subject: [PATCH 1/3] Add examples for MultiIndex.get_locs + cleanups --- pandas/core/indexes/multi.py | 41 +++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 35f738b347a3e..8692b38621b85 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1982,18 +1982,23 @@ def _partial_tup_index(self, tup, side='left'): def get_loc(self, key, method=None): """ - Get integer location, slice or boolean mask for requested label or - tuple. If the key is past the lexsort depth, the return may be a - boolean mask array, otherwise it is always a slice or int. + Get location for a label or a tuple of labels as an integer, slice or + boolean mask. + + Note that the key cannot be a slice, list of same-level labels, + a boolean mask, or a sequence of such. If you want that, use + :meth:`get_locs` instead. Parameters ---------- - key : label or tuple + key : label or tuple of labels method : None Returns ------- loc : int, slice object or boolean mask + If the key is past the lexsort depth, the return may be a + boolean mask array, otherwise it is always a slice or int. Examples --------- @@ -2117,8 +2122,7 @@ def get_loc_level(self, key, level=0, drop_level=True): See Also --------- - MultiIndex.get_loc : Get integer location, slice or boolean mask for - requested label or tuple. + MultiIndex.get_loc : Get location for a label or a tuple of labels. """ def maybe_droplevels(indexer, levels, drop_level): @@ -2330,17 +2334,34 @@ def convert_indexer(start, stop, step, indexer=indexer, labels=labels): def get_locs(self, tup): """ - Given a tuple of slices/lists/labels/boolean indexer to a level-wise - spec produce an indexer to extract those locations + Get location for a given label/slice/list/mask or a sequence of such as + a integer, slice or boolean mask. Parameters ---------- - key : tuple of (slices/list/labels) + key : label/slice/list/mask or a sequence of such. Returns ------- - locs : integer list of locations or boolean indexer suitable + locs : integer, slice or boolean mask suitable for passing to iloc + + Examples + --------- + >>> mi = pd.MultiIndex.from_arrays([list('abb'), list('def')]) + + >>> mi.get_locs('b') + slice(1, 3, None) + + >>> mi.get_locs([slice(None), ['e', 'f']]) + array([1, 2], dtype=int64) + + >>> mi.get_locs([[True, False, True], slice('e', 'f')]) + array([2], dtype=int64) + + See also + -------- + get_loc : Get location for a label or a tuple of labels. """ # must be lexsorted to at least as many levels From 6008e3c7f9388e5b0246effad108cb21df68aee8 Mon Sep 17 00:00:00 2001 From: tp Date: Tue, 26 Sep 2017 10:21:46 +0200 Subject: [PATCH 2/3] clarifies input to get_loc is sequence --- pandas/core/categorical.py | 2 +- pandas/core/indexes/multi.py | 35 ++++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/pandas/core/categorical.py b/pandas/core/categorical.py index d79937829cf3f..61e28dde2e34c 100644 --- a/pandas/core/categorical.py +++ b/pandas/core/categorical.py @@ -229,7 +229,7 @@ class Categorical(PandasObject): See also -------- - pandas.api.types.CategoricalDtype + pandas.api.types.CategoricalDtype : Type for categorical data CategoricalIndex : An Index with an underlying ``Categorical`` """ diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 8692b38621b85..25c59b11f4496 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -72,8 +72,8 @@ class MultiIndex(Index): Examples --------- A new ``MultiIndex`` is typically constructed using one of the helper - methods :meth:`MultiIndex.from_arrays``, :meth:`MultiIndex.from_product`` - and :meth:`MultiIndex.from_tuples``. For example (using ``.from_arrays``): + methods :meth:`MultiIndex.from_arrays`, :meth:`MultiIndex.from_product` + and :meth:`MultiIndex.from_tuples`. For example (using ``.from_arrays``): >>> arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']] >>> pd.MultiIndex.from_arrays(arrays, names=('number', 'color')) @@ -1985,13 +1985,9 @@ def get_loc(self, key, method=None): Get location for a label or a tuple of labels as an integer, slice or boolean mask. - Note that the key cannot be a slice, list of same-level labels, - a boolean mask, or a sequence of such. If you want that, use - :meth:`get_locs` instead. - Parameters ---------- - key : label or tuple of labels + key : label or tuple of labels (one for each level) method : None Returns @@ -2003,17 +1999,24 @@ def get_loc(self, key, method=None): Examples --------- >>> mi = pd.MultiIndex.from_arrays([list('abb'), list('def')]) + >>> mi.get_loc('b') slice(1, 3, None) + >>> mi.get_loc(('b', 'e')) 1 + Notes + ------ + The key cannot be a slice, list of same-level labels, a boolean mask, + or a sequence of such. If you want to use those, use :meth:`get_locs` + instead. + See also -------- Index.get_loc : get_loc method for (single-level) index. - get_locs : Given a tuple of slices/lists/labels/boolean indexer to a - level-wise spec, produce an indexer to extract those - locations. + get_locs : Get location for a label/slice/list/mask or a sequence of + such. """ if method is not None: raise NotImplementedError('only the default get_loc method is ' @@ -2122,7 +2125,9 @@ def get_loc_level(self, key, level=0, drop_level=True): See Also --------- - MultiIndex.get_loc : Get location for a label or a tuple of labels. + MultiIndex.get_loc : Get location for a label or a tuple of labels. + MultiIndex.get_locs : Get location for a label/slice/list/mask or a + sequence of such """ def maybe_droplevels(indexer, levels, drop_level): @@ -2332,14 +2337,14 @@ def convert_indexer(start, stop, step, indexer=indexer, labels=labels): j = labels.searchsorted(loc, side='right') return slice(i, j) - def get_locs(self, tup): + def get_locs(self, seq): """ Get location for a given label/slice/list/mask or a sequence of such as a integer, slice or boolean mask. Parameters ---------- - key : label/slice/list/mask or a sequence of such. + seq : label/slice/list/mask or a sequence of such. Returns ------- @@ -2365,7 +2370,7 @@ def get_locs(self, tup): """ # must be lexsorted to at least as many levels - true_slices = [i for (i, s) in enumerate(is_true_slices(tup)) if s] + true_slices = [i for (i, s) in enumerate(is_true_slices(seq)) if s] if true_slices and true_slices[-1] >= self.lexsort_depth: raise UnsortedIndexError('MultiIndex slicing requires the index ' 'to be lexsorted: slicing on levels {0}, ' @@ -2398,7 +2403,7 @@ def _update_indexer(idxr, indexer=indexer): return indexer return indexer & idxr - for i, k in enumerate(tup): + for i, k in enumerate(seq): if is_bool_indexer(k): # a boolean indexer, must be the same length! From 070e8f34b483fb92f6d2e64d6755a5c86860a62e Mon Sep 17 00:00:00 2001 From: tp Date: Thu, 28 Sep 2017 09:41:36 +0200 Subject: [PATCH 3/3] Small addition/cleanup + resolved merge conflict --- pandas/core/indexes/multi.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 25c59b11f4496..9ffac0832062d 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2009,14 +2009,14 @@ def get_loc(self, key, method=None): Notes ------ The key cannot be a slice, list of same-level labels, a boolean mask, - or a sequence of such. If you want to use those, use :meth:`get_locs` - instead. + or a sequence of such. If you want to use those, use + :meth:`MultiIndex.get_locs` instead. See also -------- Index.get_loc : get_loc method for (single-level) index. - get_locs : Get location for a label/slice/list/mask or a sequence of - such. + MultiIndex.get_locs : Get location for a label/slice/list/mask or a + sequence of such. """ if method is not None: raise NotImplementedError('only the default get_loc method is ' @@ -2340,23 +2340,24 @@ def convert_indexer(start, stop, step, indexer=indexer, labels=labels): def get_locs(self, seq): """ Get location for a given label/slice/list/mask or a sequence of such as - a integer, slice or boolean mask. + an array of integers. Parameters ---------- - seq : label/slice/list/mask or a sequence of such. + seq : label/slice/list/mask or a sequence of such + You should use one of the above for each level. + If a level should not be used, set it to ``slice(None)``. Returns ------- - locs : integer, slice or boolean mask suitable - for passing to iloc + locs : array of integers suitable for passing to iloc Examples --------- >>> mi = pd.MultiIndex.from_arrays([list('abb'), list('def')]) >>> mi.get_locs('b') - slice(1, 3, None) + array([1, 2], dtype=int64) >>> mi.get_locs([slice(None), ['e', 'f']]) array([1, 2], dtype=int64) @@ -2366,7 +2367,7 @@ def get_locs(self, seq): See also -------- - get_loc : Get location for a label or a tuple of labels. + MultiIndex.get_loc : Get location for a label or a tuple of labels. """ # must be lexsorted to at least as many levels