-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
DEPR MultiIndex.is_lexsorted and MultiIndex.lexsort_depth #38701
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
Changes from all commits
b5d157a
8ca3b3d
109c1ce
238b568
893ab7c
b2b1c36
b8bcc5e
4ab8981
72b0abc
a0a709c
be2a692
7d1d279
b3c5144
39d163f
6b9c698
74d8c7e
3798cf5
dfc779d
fdbb366
fff7297
fe4aaa6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -391,11 +391,11 @@ def _verify_integrity( | |
f"Level values must be unique: {list(level)} on level {i}" | ||
) | ||
if self.sortorder is not None: | ||
if self.sortorder > self._lexsort_depth(): | ||
if self.sortorder > _lexsort_depth(self.codes, self.nlevels): | ||
raise ValueError( | ||
"Value for sortorder must be inferior or equal to actual " | ||
f"lexsort_depth: sortorder {self.sortorder} " | ||
f"with lexsort_depth {self._lexsort_depth()}" | ||
f"with lexsort_depth {_lexsort_depth(self.codes, self.nlevels)}" | ||
) | ||
|
||
codes = [ | ||
|
@@ -1809,6 +1809,15 @@ def _is_all_dates(self) -> bool: | |
return False | ||
|
||
def is_lexsorted(self) -> bool: | ||
warnings.warn( | ||
"MultiIndex.is_lexsorted is deprecated as a public function, " | ||
"users should use MultiIndex.is_monotonic_increasing instead.", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._is_lexsorted() | ||
|
||
def _is_lexsorted(self) -> bool: | ||
""" | ||
Return True if the codes are lexicographically sorted. | ||
|
||
|
@@ -1840,15 +1849,19 @@ def is_lexsorted(self) -> bool: | |
... ['bb', 'aa', 'aa', 'bb']]).is_lexsorted() | ||
False | ||
""" | ||
return self.lexsort_depth == self.nlevels | ||
return self._lexsort_depth == self.nlevels | ||
|
||
@cache_readonly | ||
@property | ||
def lexsort_depth(self): | ||
if self.sortorder is not None: | ||
return self.sortorder | ||
|
||
return self._lexsort_depth() | ||
warnings.warn( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you don't need the cache on this one (as its already on _lexsort_depth) and this is now user facing |
||
"MultiIndex.is_lexsorted is deprecated as a public function, " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MarcoGorelli should the message here refer to lexsort_depth instead of is_lexsorted? this warning isn't being caught in a bunch of tests; not sure why that isn't caught by the npdev build There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, looks like a typo, thanks for catching - we probably want to always use
not sure what you mean here sorry |
||
"users should use MultiIndex.is_monotonic_increasing instead.", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._lexsort_depth | ||
|
||
@cache_readonly | ||
def _lexsort_depth(self) -> int: | ||
""" | ||
Compute and return the lexsort_depth, the number of levels of the | ||
|
@@ -1858,11 +1871,9 @@ def _lexsort_depth(self) -> int: | |
------- | ||
int | ||
""" | ||
int64_codes = [ensure_int64(level_codes) for level_codes in self.codes] | ||
for k in range(self.nlevels, 0, -1): | ||
if libalgos.is_lexsorted(int64_codes[:k]): | ||
return k | ||
return 0 | ||
if self.sortorder is not None: | ||
return self.sortorder | ||
return _lexsort_depth(self.codes, self.nlevels) | ||
|
||
def _sort_levels_monotonic(self): | ||
""" | ||
|
@@ -1898,7 +1909,7 @@ def _sort_levels_monotonic(self): | |
('b', 'bb')], | ||
) | ||
""" | ||
if self.is_lexsorted() and self.is_monotonic: | ||
if self._is_lexsorted() and self.is_monotonic: | ||
return self | ||
|
||
new_levels = [] | ||
|
@@ -2184,7 +2195,7 @@ def drop(self, codes, level=None, errors="raise"): | |
step = loc.step if loc.step is not None else 1 | ||
inds.extend(range(loc.start, loc.stop, step)) | ||
elif com.is_bool_indexer(loc): | ||
if self.lexsort_depth == 0: | ||
if self._lexsort_depth == 0: | ||
warnings.warn( | ||
"dropping on a non-lexsorted multi-index " | ||
"without a level parameter may impact performance.", | ||
|
@@ -2755,10 +2766,10 @@ def slice_locs(self, start=None, end=None, step=None, kind=None): | |
return super().slice_locs(start, end, step, kind=kind) | ||
|
||
def _partial_tup_index(self, tup, side="left"): | ||
if len(tup) > self.lexsort_depth: | ||
if len(tup) > self._lexsort_depth: | ||
raise UnsortedIndexError( | ||
f"Key length ({len(tup)}) was greater than MultiIndex lexsort depth " | ||
f"({self.lexsort_depth})" | ||
f"({self._lexsort_depth})" | ||
) | ||
|
||
n = len(tup) | ||
|
@@ -2897,7 +2908,7 @@ def _maybe_to_slice(loc): | |
# break the key into 2 parts based on the lexsort_depth of the index; | ||
# the first part returns a continuous slice of the index; the 2nd part | ||
# needs linear search within the slice | ||
i = self.lexsort_depth | ||
i = self._lexsort_depth | ||
lead_key, follow_key = key[:i], key[i:] | ||
start, stop = ( | ||
self.slice_locs(lead_key, lead_key) if lead_key else (0, len(self)) | ||
|
@@ -3150,7 +3161,7 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): | |
stop = getattr(stop, "stop", stop) | ||
return convert_indexer(start, stop, step) | ||
|
||
elif level > 0 or self.lexsort_depth == 0 or step is not None: | ||
elif level > 0 or self._lexsort_depth == 0 or step is not None: | ||
# need to have like semantics here to right | ||
# searching as when we are using a slice | ||
# so include the stop+1 (so we include stop) | ||
|
@@ -3165,7 +3176,7 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes): | |
|
||
idx = self._get_loc_single_level_index(level_index, key) | ||
|
||
if level > 0 or self.lexsort_depth == 0: | ||
if level > 0 or self._lexsort_depth == 0: | ||
# Desired level is not sorted | ||
locs = np.array(level_codes == idx, dtype=bool, copy=False) | ||
if not locs.any(): | ||
|
@@ -3222,10 +3233,10 @@ def get_locs(self, seq): | |
|
||
# must be lexsorted to at least as many levels | ||
true_slices = [i for (i, s) in enumerate(com.is_true_slices(seq)) if s] | ||
if true_slices and true_slices[-1] >= self.lexsort_depth: | ||
if true_slices and true_slices[-1] >= self._lexsort_depth: | ||
raise UnsortedIndexError( | ||
"MultiIndex slicing requires the index to be lexsorted: slicing " | ||
f"on levels {true_slices}, lexsort depth {self.lexsort_depth}" | ||
f"on levels {true_slices}, lexsort depth {self._lexsort_depth}" | ||
) | ||
# indexer | ||
# this is the list of all values that we want to select | ||
|
@@ -3347,7 +3358,7 @@ def _reorder_indexer( | |
""" | ||
# If the index is lexsorted and the list_like label in seq are sorted | ||
# then we do not need to sort | ||
if self.is_lexsorted(): | ||
if self._is_lexsorted(): | ||
need_sort = False | ||
for i, k in enumerate(seq): | ||
if is_list_like(k): | ||
|
@@ -3768,6 +3779,15 @@ def isin(self, values, level=None): | |
__inv__ = make_invalid_op("__inv__") | ||
|
||
|
||
def _lexsort_depth(codes: List[np.ndarray], nlevels: int) -> int: | ||
"""Count depth (up to a maximum of `nlevels`) with which codes are lexsorted.""" | ||
int64_codes = [ensure_int64(level_codes) for level_codes in codes] | ||
for k in range(nlevels, 0, -1): | ||
if libalgos.is_lexsorted(int64_codes[:k]): | ||
return k | ||
return 0 | ||
|
||
|
||
def sparsify_labels(label_list, start: int = 0, sentinel=""): | ||
pivoted = list(zip(*label_list)) | ||
k = len(label_list) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
metion in the whatsnew as well