@@ -83,11 +83,14 @@ def get_indexer_indexer(
8383
8484 if level is not None :
8585 _ , indexer = target .sortlevel (
86- level , ascending = ascending , sort_remaining = sort_remaining
86+ level ,
87+ ascending = ascending ,
88+ sort_remaining = sort_remaining ,
89+ na_position = na_position ,
8790 )
8891 elif isinstance (target , ABCMultiIndex ):
8992 indexer = lexsort_indexer (
90- target ._get_codes_for_sorting () , orders = ascending , na_position = na_position
93+ target .codes , orders = ascending , na_position = na_position , codes_given = True
9194 )
9295 else :
9396 # Check monotonic-ness before sort an index (GH 11080)
@@ -302,7 +305,11 @@ def indexer_from_factorized(
302305
303306
304307def lexsort_indexer (
305- keys , orders = None , na_position : str = "last" , key : Callable | None = None
308+ keys ,
309+ orders = None ,
310+ na_position : str = "last" ,
311+ key : Callable | None = None ,
312+ codes_given : bool = False ,
306313) -> npt .NDArray [np .intp ]:
307314 """
308315 Performs lexical sorting on a set of keys
@@ -321,6 +328,8 @@ def lexsort_indexer(
321328 Determines placement of NA elements in the sorted list ("last" or "first")
322329 key : Callable, optional
323330 Callable key function applied to every element in keys before sorting
331+ codes_given: bool, False
332+ Avoid categorical materialization if codes are already provided.
324333
325334 Returns
326335 -------
@@ -338,15 +347,27 @@ def lexsort_indexer(
338347 keys = [ensure_key_mapped (k , key ) for k in keys ]
339348
340349 for k , order in zip (keys , orders ):
341- cat = Categorical (k , ordered = True )
342-
343350 if na_position not in ["last" , "first" ]:
344351 raise ValueError (f"invalid na_position: { na_position } " )
345352
346- n = len (cat .categories )
347- codes = cat .codes .copy ()
353+ if codes_given :
354+ mask = k == - 1
355+ codes = k .copy ()
356+ n = len (codes )
357+ mask_n = n
358+ if mask .any ():
359+ n -= 1
360+
361+ else :
362+ cat = Categorical (k , ordered = True )
363+ n = len (cat .categories )
364+ codes = cat .codes .copy ()
365+ mask = cat .codes == - 1
366+ if mask .any ():
367+ mask_n = n + 1
368+ else :
369+ mask_n = n
348370
349- mask = cat .codes == - 1
350371 if order : # ascending
351372 if na_position == "last" :
352373 codes = np .where (mask , n , codes )
@@ -357,10 +378,8 @@ def lexsort_indexer(
357378 codes = np .where (mask , n , n - codes - 1 )
358379 elif na_position == "first" :
359380 codes = np .where (mask , 0 , n - codes )
360- if mask .any ():
361- n += 1
362381
363- shape .append (n )
382+ shape .append (mask_n )
364383 labels .append (codes )
365384
366385 return indexer_from_factorized (labels , tuple (shape ))
0 commit comments