@@ -391,11 +391,11 @@ def _verify_integrity(
391
391
f"Level values must be unique: { list (level )} on level { i } "
392
392
)
393
393
if self .sortorder is not None :
394
- if self .sortorder > self . _lexsort_depth ():
394
+ if self .sortorder > _lexsort_depth (self . codes , self . nlevels ):
395
395
raise ValueError (
396
396
"Value for sortorder must be inferior or equal to actual "
397
397
f"lexsort_depth: sortorder { self .sortorder } "
398
- f"with lexsort_depth { self . _lexsort_depth ()} "
398
+ f"with lexsort_depth { _lexsort_depth (self . codes , self . nlevels )} "
399
399
)
400
400
401
401
codes = [
@@ -1809,6 +1809,15 @@ def _is_all_dates(self) -> bool:
1809
1809
return False
1810
1810
1811
1811
def is_lexsorted (self ) -> bool :
1812
+ warnings .warn (
1813
+ "MultiIndex.is_lexsorted is deprecated as a public function, "
1814
+ "users should use MultiIndex.is_monotonic_increasing instead." ,
1815
+ FutureWarning ,
1816
+ stacklevel = 2 ,
1817
+ )
1818
+ return self ._is_lexsorted ()
1819
+
1820
+ def _is_lexsorted (self ) -> bool :
1812
1821
"""
1813
1822
Return True if the codes are lexicographically sorted.
1814
1823
@@ -1840,15 +1849,19 @@ def is_lexsorted(self) -> bool:
1840
1849
... ['bb', 'aa', 'aa', 'bb']]).is_lexsorted()
1841
1850
False
1842
1851
"""
1843
- return self .lexsort_depth == self .nlevels
1852
+ return self ._lexsort_depth == self .nlevels
1844
1853
1845
- @cache_readonly
1854
+ @property
1846
1855
def lexsort_depth (self ):
1847
- if self .sortorder is not None :
1848
- return self .sortorder
1849
-
1850
- return self ._lexsort_depth ()
1856
+ warnings .warn (
1857
+ "MultiIndex.is_lexsorted is deprecated as a public function, "
1858
+ "users should use MultiIndex.is_monotonic_increasing instead." ,
1859
+ FutureWarning ,
1860
+ stacklevel = 2 ,
1861
+ )
1862
+ return self ._lexsort_depth
1851
1863
1864
+ @cache_readonly
1852
1865
def _lexsort_depth (self ) -> int :
1853
1866
"""
1854
1867
Compute and return the lexsort_depth, the number of levels of the
@@ -1858,11 +1871,9 @@ def _lexsort_depth(self) -> int:
1858
1871
-------
1859
1872
int
1860
1873
"""
1861
- int64_codes = [ensure_int64 (level_codes ) for level_codes in self .codes ]
1862
- for k in range (self .nlevels , 0 , - 1 ):
1863
- if libalgos .is_lexsorted (int64_codes [:k ]):
1864
- return k
1865
- return 0
1874
+ if self .sortorder is not None :
1875
+ return self .sortorder
1876
+ return _lexsort_depth (self .codes , self .nlevels )
1866
1877
1867
1878
def _sort_levels_monotonic (self ):
1868
1879
"""
@@ -1898,7 +1909,7 @@ def _sort_levels_monotonic(self):
1898
1909
('b', 'bb')],
1899
1910
)
1900
1911
"""
1901
- if self .is_lexsorted () and self .is_monotonic :
1912
+ if self ._is_lexsorted () and self .is_monotonic :
1902
1913
return self
1903
1914
1904
1915
new_levels = []
@@ -2184,7 +2195,7 @@ def drop(self, codes, level=None, errors="raise"):
2184
2195
step = loc .step if loc .step is not None else 1
2185
2196
inds .extend (range (loc .start , loc .stop , step ))
2186
2197
elif com .is_bool_indexer (loc ):
2187
- if self .lexsort_depth == 0 :
2198
+ if self ._lexsort_depth == 0 :
2188
2199
warnings .warn (
2189
2200
"dropping on a non-lexsorted multi-index "
2190
2201
"without a level parameter may impact performance." ,
@@ -2755,10 +2766,10 @@ def slice_locs(self, start=None, end=None, step=None, kind=None):
2755
2766
return super ().slice_locs (start , end , step , kind = kind )
2756
2767
2757
2768
def _partial_tup_index (self , tup , side = "left" ):
2758
- if len (tup ) > self .lexsort_depth :
2769
+ if len (tup ) > self ._lexsort_depth :
2759
2770
raise UnsortedIndexError (
2760
2771
f"Key length ({ len (tup )} ) was greater than MultiIndex lexsort depth "
2761
- f"({ self .lexsort_depth } )"
2772
+ f"({ self ._lexsort_depth } )"
2762
2773
)
2763
2774
2764
2775
n = len (tup )
@@ -2897,7 +2908,7 @@ def _maybe_to_slice(loc):
2897
2908
# break the key into 2 parts based on the lexsort_depth of the index;
2898
2909
# the first part returns a continuous slice of the index; the 2nd part
2899
2910
# needs linear search within the slice
2900
- i = self .lexsort_depth
2911
+ i = self ._lexsort_depth
2901
2912
lead_key , follow_key = key [:i ], key [i :]
2902
2913
start , stop = (
2903
2914
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):
3150
3161
stop = getattr (stop , "stop" , stop )
3151
3162
return convert_indexer (start , stop , step )
3152
3163
3153
- elif level > 0 or self .lexsort_depth == 0 or step is not None :
3164
+ elif level > 0 or self ._lexsort_depth == 0 or step is not None :
3154
3165
# need to have like semantics here to right
3155
3166
# searching as when we are using a slice
3156
3167
# so include the stop+1 (so we include stop)
@@ -3165,7 +3176,7 @@ def convert_indexer(start, stop, step, indexer=indexer, codes=level_codes):
3165
3176
3166
3177
idx = self ._get_loc_single_level_index (level_index , key )
3167
3178
3168
- if level > 0 or self .lexsort_depth == 0 :
3179
+ if level > 0 or self ._lexsort_depth == 0 :
3169
3180
# Desired level is not sorted
3170
3181
locs = np .array (level_codes == idx , dtype = bool , copy = False )
3171
3182
if not locs .any ():
@@ -3222,10 +3233,10 @@ def get_locs(self, seq):
3222
3233
3223
3234
# must be lexsorted to at least as many levels
3224
3235
true_slices = [i for (i , s ) in enumerate (com .is_true_slices (seq )) if s ]
3225
- if true_slices and true_slices [- 1 ] >= self .lexsort_depth :
3236
+ if true_slices and true_slices [- 1 ] >= self ._lexsort_depth :
3226
3237
raise UnsortedIndexError (
3227
3238
"MultiIndex slicing requires the index to be lexsorted: slicing "
3228
- f"on levels { true_slices } , lexsort depth { self .lexsort_depth } "
3239
+ f"on levels { true_slices } , lexsort depth { self ._lexsort_depth } "
3229
3240
)
3230
3241
# indexer
3231
3242
# this is the list of all values that we want to select
@@ -3347,7 +3358,7 @@ def _reorder_indexer(
3347
3358
"""
3348
3359
# If the index is lexsorted and the list_like label in seq are sorted
3349
3360
# then we do not need to sort
3350
- if self .is_lexsorted ():
3361
+ if self ._is_lexsorted ():
3351
3362
need_sort = False
3352
3363
for i , k in enumerate (seq ):
3353
3364
if is_list_like (k ):
@@ -3768,6 +3779,15 @@ def isin(self, values, level=None):
3768
3779
__inv__ = make_invalid_op ("__inv__" )
3769
3780
3770
3781
3782
+ def _lexsort_depth (codes : List [np .ndarray ], nlevels : int ) -> int :
3783
+ """Count depth (up to a maximum of `nlevels`) with which codes are lexsorted."""
3784
+ int64_codes = [ensure_int64 (level_codes ) for level_codes in codes ]
3785
+ for k in range (nlevels , 0 , - 1 ):
3786
+ if libalgos .is_lexsorted (int64_codes [:k ]):
3787
+ return k
3788
+ return 0
3789
+
3790
+
3771
3791
def sparsify_labels (label_list , start : int = 0 , sentinel = "" ):
3772
3792
pivoted = list (zip (* label_list ))
3773
3793
k = len (label_list )
0 commit comments