11
11
from pandas ._config import get_option
12
12
13
13
from pandas ._libs import index as libindex
14
+ from pandas ._libs .lib import no_default
14
15
from pandas ._typing import (
15
16
ArrayLike ,
16
17
Dtype ,
@@ -233,6 +234,22 @@ def __new__(
233
234
234
235
# --------------------------------------------------------------------
235
236
237
+ @doc (Index ._shallow_copy )
238
+ def _shallow_copy (
239
+ self ,
240
+ values : Categorical ,
241
+ name : Hashable = no_default ,
242
+ ) -> CategoricalIndex :
243
+ name = self ._name if name is no_default else name
244
+
245
+ if values is not None :
246
+ # In tests we only get here with Categorical objects that
247
+ # have matching .ordered, and values.categories a subset of
248
+ # our own. However we do _not_ have a dtype match in general.
249
+ values = Categorical (values , dtype = self .dtype )
250
+
251
+ return super ()._shallow_copy (values = values , name = name )
252
+
236
253
def _is_dtype_compat (self , other ) -> Categorical :
237
254
"""
238
255
*this is an internal non-public method*
@@ -369,6 +386,15 @@ def fillna(self, value, downcast=None):
369
386
370
387
return type (self )._simple_new (cat , name = self .name )
371
388
389
+ @doc (Index .unique )
390
+ def unique (self , level = None ):
391
+ if level is not None :
392
+ self ._validate_index_level (level )
393
+ result = self ._values .unique ()
394
+ # Use _simple_new instead of _shallow_copy to ensure we keep dtype
395
+ # of result, not self.
396
+ return type (self )._simple_new (result , name = self .name )
397
+
372
398
def reindex (
373
399
self , target , method = None , level = None , limit = None , tolerance = None
374
400
) -> tuple [Index , np .ndarray | None ]:
@@ -431,8 +457,8 @@ def reindex(
431
457
# in which case we are going to conform to the passed Categorical
432
458
new_target = np .asarray (new_target )
433
459
if is_categorical_dtype (target ):
434
- cat = Categorical (new_target , dtype = target .dtype )
435
- new_target = type (self )._simple_new (cat , name = self .name )
460
+ new_target = Categorical (new_target , dtype = target .dtype )
461
+ new_target = type (self )._simple_new (new_target , name = self .name )
436
462
else :
437
463
new_target = Index (new_target , name = self .name )
438
464
@@ -482,23 +508,18 @@ def _get_indexer(
482
508
limit : int | None = None ,
483
509
tolerance = None ,
484
510
) -> np .ndarray :
485
- # returned ndarray is np.intp
486
511
487
512
if self .equals (target ):
488
513
return np .arange (len (self ), dtype = "intp" )
489
514
490
515
return self ._get_indexer_non_unique (target ._values )[0 ]
491
516
492
517
@Appender (_index_shared_docs ["get_indexer_non_unique" ] % _index_doc_kwargs )
493
- def get_indexer_non_unique (self , target ) -> tuple [np .ndarray , np .ndarray ]:
494
- # both returned ndarrays are np.intp
518
+ def get_indexer_non_unique (self , target ):
495
519
target = ibase .ensure_index (target )
496
520
return self ._get_indexer_non_unique (target ._values )
497
521
498
- def _get_indexer_non_unique (
499
- self , values : ArrayLike
500
- ) -> tuple [np .ndarray , np .ndarray ]:
501
- # both returned ndarrays are np.intp
522
+ def _get_indexer_non_unique (self , values : ArrayLike ):
502
523
"""
503
524
get_indexer_non_unique but after unrapping the target Index object.
504
525
"""
@@ -517,7 +538,7 @@ def _get_indexer_non_unique(
517
538
codes = self .categories .get_indexer (values )
518
539
519
540
indexer , missing = self ._engine .get_indexer_non_unique (codes )
520
- return ensure_platform_int (indexer ), ensure_platform_int ( missing )
541
+ return ensure_platform_int (indexer ), missing
521
542
522
543
@doc (Index ._convert_list_indexer )
523
544
def _convert_list_indexer (self , keyarr ):
0 commit comments