Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pandas/core/indexes/category.py : 29%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from typing import Any, List
2import warnings
4import numpy as np
6from pandas._config import get_option
8from pandas._libs import index as libindex
9from pandas._libs.hashtable import duplicated_int64
10from pandas._typing import AnyArrayLike
11from pandas.util._decorators import Appender, cache_readonly
13from pandas.core.dtypes.common import (
14 ensure_platform_int,
15 is_categorical_dtype,
16 is_interval_dtype,
17 is_list_like,
18 is_scalar,
19)
20from pandas.core.dtypes.dtypes import CategoricalDtype
21from pandas.core.dtypes.generic import ABCCategorical, ABCSeries
22from pandas.core.dtypes.missing import isna
24from pandas.core import accessor
25from pandas.core.algorithms import take_1d
26from pandas.core.arrays.categorical import Categorical, _recode_for_categories, contains
27import pandas.core.common as com
28import pandas.core.indexes.base as ibase
29from pandas.core.indexes.base import Index, _index_shared_docs, maybe_extract_name
30from pandas.core.indexes.extension import ExtensionIndex, inherit_names
31import pandas.core.missing as missing
32from pandas.core.ops import get_op_result_name
34_index_doc_kwargs = dict(ibase._index_doc_kwargs)
35_index_doc_kwargs.update(dict(target_klass="CategoricalIndex"))
38@inherit_names(
39 [
40 "argsort",
41 "_internal_get_values",
42 "tolist",
43 "codes",
44 "categories",
45 "ordered",
46 "_reverse_indexer",
47 "searchsorted",
48 "is_dtype_equal",
49 "min",
50 "max",
51 ],
52 Categorical,
53)
54@accessor.delegate_names(
55 delegate=Categorical,
56 accessors=[
57 "rename_categories",
58 "reorder_categories",
59 "add_categories",
60 "remove_categories",
61 "remove_unused_categories",
62 "set_categories",
63 "as_ordered",
64 "as_unordered",
65 ],
66 typ="method",
67 overwrite=True,
68)
69class CategoricalIndex(ExtensionIndex, accessor.PandasDelegate):
70 """
71 Index based on an underlying :class:`Categorical`.
73 CategoricalIndex, like Categorical, can only take on a limited,
74 and usually fixed, number of possible values (`categories`). Also,
75 like Categorical, it might have an order, but numerical operations
76 (additions, divisions, ...) are not possible.
78 Parameters
79 ----------
80 data : array-like (1-dimensional)
81 The values of the categorical. If `categories` are given, values not in
82 `categories` will be replaced with NaN.
83 categories : index-like, optional
84 The categories for the categorical. Items need to be unique.
85 If the categories are not given here (and also not in `dtype`), they
86 will be inferred from the `data`.
87 ordered : bool, optional
88 Whether or not this categorical is treated as an ordered
89 categorical. If not given here or in `dtype`, the resulting
90 categorical will be unordered.
91 dtype : CategoricalDtype or "category", optional
92 If :class:`CategoricalDtype`, cannot be used together with
93 `categories` or `ordered`.
95 .. versionadded:: 0.21.0
96 copy : bool, default False
97 Make a copy of input ndarray.
98 name : object, optional
99 Name to be stored in the index.
101 Attributes
102 ----------
103 codes
104 categories
105 ordered
107 Methods
108 -------
109 rename_categories
110 reorder_categories
111 add_categories
112 remove_categories
113 remove_unused_categories
114 set_categories
115 as_ordered
116 as_unordered
117 map
119 Raises
120 ------
121 ValueError
122 If the categories do not validate.
123 TypeError
124 If an explicit ``ordered=True`` is given but no `categories` and the
125 `values` are not sortable.
127 See Also
128 --------
129 Index : The base pandas Index type.
130 Categorical : A categorical array.
131 CategoricalDtype : Type for categorical data.
133 Notes
134 -----
135 See the `user guide
136 <https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html#categoricalindex>`_
137 for more.
139 Examples
140 --------
141 >>> pd.CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'])
142 CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], ordered=False, dtype='category') # noqa
144 ``CategoricalIndex`` can also be instantiated from a ``Categorical``:
146 >>> c = pd.Categorical(['a', 'b', 'c', 'a', 'b', 'c'])
147 >>> pd.CategoricalIndex(c)
148 CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], ordered=False, dtype='category') # noqa
150 Ordered ``CategoricalIndex`` can have a min and max value.
152 >>> ci = pd.CategoricalIndex(['a','b','c','a','b','c'], ordered=True,
153 ... categories=['c', 'b', 'a'])
154 >>> ci
155 CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['c', 'b', 'a'], ordered=True, dtype='category') # noqa
156 >>> ci.min()
157 'c'
158 """
160 _typ = "categoricalindex"
162 _raw_inherit = {
163 "argsort",
164 "_internal_get_values",
165 "tolist",
166 "codes",
167 "categories",
168 "ordered",
169 "_reverse_indexer",
170 "searchsorted",
171 }
173 codes: np.ndarray
174 categories: Index
176 @property
177 def _engine_type(self):
178 # self.codes can have dtype int8, int16, int32 or int64, so we need
179 # to return the corresponding engine type (libindex.Int8Engine, etc.).
180 return {
181 np.int8: libindex.Int8Engine,
182 np.int16: libindex.Int16Engine,
183 np.int32: libindex.Int32Engine,
184 np.int64: libindex.Int64Engine,
185 }[self.codes.dtype.type]
187 _attributes = ["name"]
189 # --------------------------------------------------------------------
190 # Constructors
192 def __new__(
193 cls, data=None, categories=None, ordered=None, dtype=None, copy=False, name=None
194 ):
196 dtype = CategoricalDtype._from_values_or_dtype(data, categories, ordered, dtype)
198 name = maybe_extract_name(name, data, cls)
200 if not is_categorical_dtype(data):
201 # don't allow scalars
202 # if data is None, then categories must be provided
203 if is_scalar(data):
204 if data is not None or categories is None:
205 raise cls._scalar_data_error(data)
206 data = []
208 data = cls._create_categorical(data, dtype=dtype)
210 data = data.copy() if copy else data
212 return cls._simple_new(data, name=name)
214 def _create_from_codes(self, codes, dtype=None, name=None):
215 """
216 *this is an internal non-public method*
218 create the correct categorical from codes
220 Parameters
221 ----------
222 codes : new codes
223 dtype: CategoricalDtype, defaults to existing
224 name : optional name attribute, defaults to existing
226 Returns
227 -------
228 CategoricalIndex
229 """
231 if dtype is None:
232 dtype = self.dtype
233 if name is None:
234 name = self.name
235 cat = Categorical.from_codes(codes, dtype=dtype)
236 return CategoricalIndex(cat, name=name)
238 @classmethod
239 def _create_categorical(cls, data, dtype=None):
240 """
241 *this is an internal non-public method*
243 create the correct categorical from data and the properties
245 Parameters
246 ----------
247 data : data for new Categorical
248 dtype : CategoricalDtype, defaults to existing
250 Returns
251 -------
252 Categorical
253 """
254 if isinstance(data, (cls, ABCSeries)) and is_categorical_dtype(data):
255 data = data.values
257 if not isinstance(data, ABCCategorical):
258 return Categorical(data, dtype=dtype)
260 if isinstance(dtype, CategoricalDtype) and dtype != data.dtype:
261 # we want to silently ignore dtype='category'
262 data = data._set_dtype(dtype)
263 return data
265 @classmethod
266 def _simple_new(cls, values, name=None, dtype=None):
267 result = object.__new__(cls)
269 values = cls._create_categorical(values, dtype=dtype)
270 result._data = values
271 result.name = name
273 result._reset_identity()
274 result._no_setting_name = False
275 return result
277 # --------------------------------------------------------------------
279 @Appender(_index_shared_docs["_shallow_copy"])
280 def _shallow_copy(self, values=None, dtype=None, **kwargs):
281 if dtype is None:
282 dtype = self.dtype
283 return super()._shallow_copy(values=values, dtype=dtype, **kwargs)
285 def _is_dtype_compat(self, other) -> bool:
286 """
287 *this is an internal non-public method*
289 provide a comparison between the dtype of self and other (coercing if
290 needed)
292 Raises
293 ------
294 TypeError if the dtypes are not compatible
295 """
296 if is_categorical_dtype(other):
297 if isinstance(other, CategoricalIndex):
298 other = other._values
299 if not other.is_dtype_equal(self):
300 raise TypeError(
301 "categories must match existing categories when appending"
302 )
303 else:
304 values = other
305 if not is_list_like(values):
306 values = [values]
307 other = CategoricalIndex(self._create_categorical(other, dtype=self.dtype))
308 if not other.isin(values).all():
309 raise TypeError(
310 "cannot append a non-category item to a CategoricalIndex"
311 )
313 return other
315 def equals(self, other):
316 """
317 Determine if two CategoricalIndex objects contain the same elements.
319 Returns
320 -------
321 bool
322 If two CategoricalIndex objects have equal elements True,
323 otherwise False.
324 """
325 if self.is_(other):
326 return True
328 if not isinstance(other, Index):
329 return False
331 try:
332 other = self._is_dtype_compat(other)
333 if isinstance(other, type(self)):
334 other = other._data
335 return self._data.equals(other)
336 except (TypeError, ValueError):
337 pass
339 return False
341 # --------------------------------------------------------------------
342 # Rendering Methods
344 @property
345 def _formatter_func(self):
346 return self.categories._formatter_func
348 def _format_attrs(self):
349 """
350 Return a list of tuples of the (attr,formatted_value)
351 """
352 max_categories = (
353 10
354 if get_option("display.max_categories") == 0
355 else get_option("display.max_categories")
356 )
357 attrs = [
358 (
359 "categories",
360 ibase.default_pprint(self.categories, max_seq_items=max_categories),
361 ),
362 ("ordered", self.ordered),
363 ]
364 if self.name is not None:
365 attrs.append(("name", ibase.default_pprint(self.name)))
366 attrs.append(("dtype", f"'{self.dtype.name}'"))
367 max_seq_items = get_option("display.max_seq_items") or len(self)
368 if len(self) > max_seq_items:
369 attrs.append(("length", len(self)))
370 return attrs
372 # --------------------------------------------------------------------
374 @property
375 def inferred_type(self) -> str:
376 return "categorical"
378 @property
379 def values(self):
380 """ return the underlying data, which is a Categorical """
381 return self._data
383 @property
384 def _has_complex_internals(self):
385 # used to avoid libreduction code paths, which raise or require conversion
386 return True
388 def _wrap_setop_result(self, other, result):
389 name = get_op_result_name(self, other)
390 # We use _shallow_copy rather than the Index implementation
391 # (which uses _constructor) in order to preserve dtype.
392 return self._shallow_copy(result, name=name)
394 @Appender(_index_shared_docs["contains"] % _index_doc_kwargs)
395 def __contains__(self, key) -> bool:
396 # if key is a NaN, check if any NaN is in self.
397 if is_scalar(key) and isna(key):
398 return self.hasnans
400 return contains(self, key, container=self._engine)
402 def __array__(self, dtype=None) -> np.ndarray:
403 """ the array interface, return my values """
404 return np.array(self._data, dtype=dtype)
406 @Appender(_index_shared_docs["astype"])
407 def astype(self, dtype, copy=True):
408 if is_interval_dtype(dtype):
409 from pandas import IntervalIndex
411 return IntervalIndex(np.array(self))
412 elif is_categorical_dtype(dtype):
413 # GH 18630
414 dtype = self.dtype.update_dtype(dtype)
415 if dtype == self.dtype:
416 return self.copy() if copy else self
418 return Index.astype(self, dtype=dtype, copy=copy)
420 @cache_readonly
421 def _isnan(self):
422 """ return if each value is nan"""
423 return self._data.codes == -1
425 @Appender(ibase._index_shared_docs["fillna"])
426 def fillna(self, value, downcast=None):
427 self._assert_can_do_op(value)
428 return CategoricalIndex(self._data.fillna(value), name=self.name)
430 @cache_readonly
431 def _engine(self):
432 # we are going to look things up with the codes themselves.
433 # To avoid a reference cycle, bind `codes` to a local variable, so
434 # `self` is not passed into the lambda.
435 codes = self.codes
436 return self._engine_type(lambda: codes, len(self))
438 # introspection
439 @cache_readonly
440 def is_unique(self) -> bool:
441 return self._engine.is_unique
443 @property
444 def is_monotonic_increasing(self):
445 return self._engine.is_monotonic_increasing
447 @property
448 def is_monotonic_decreasing(self) -> bool:
449 return self._engine.is_monotonic_decreasing
451 @Appender(_index_shared_docs["index_unique"] % _index_doc_kwargs)
452 def unique(self, level=None):
453 if level is not None:
454 self._validate_index_level(level)
455 result = self.values.unique()
456 # CategoricalIndex._shallow_copy keeps original dtype
457 # if not otherwise specified
458 return self._shallow_copy(result, dtype=result.dtype)
460 @Appender(Index.duplicated.__doc__)
461 def duplicated(self, keep="first"):
462 codes = self.codes.astype("i8")
463 return duplicated_int64(codes, keep)
465 def _to_safe_for_reshape(self):
466 """ convert to object if we are a categorical """
467 return self.astype("object")
469 def get_loc(self, key, method=None):
470 """
471 Get integer location, slice or boolean mask for requested label.
473 Parameters
474 ----------
475 key : label
476 method : {None}
477 * default: exact matches only.
479 Returns
480 -------
481 loc : int if unique index, slice if monotonic index, else mask
483 Raises
484 ------
485 KeyError : if the key is not in the index
487 Examples
488 --------
489 >>> unique_index = pd.CategoricalIndex(list('abc'))
490 >>> unique_index.get_loc('b')
491 1
493 >>> monotonic_index = pd.CategoricalIndex(list('abbc'))
494 >>> monotonic_index.get_loc('b')
495 slice(1, 3, None)
497 >>> non_monotonic_index = pd.CategoricalIndex(list('abcb'))
498 >>> non_monotonic_index.get_loc('b')
499 array([False, True, False, True], dtype=bool)
500 """
501 code = self.categories.get_loc(key)
502 code = self.codes.dtype.type(code)
503 try:
504 return self._engine.get_loc(code)
505 except KeyError:
506 raise KeyError(key)
508 def get_value(self, series: AnyArrayLike, key: Any):
509 """
510 Fast lookup of value from 1-dimensional ndarray. Only use this if you
511 know what you're doing
513 Parameters
514 ----------
515 series : Series, ExtensionArray, Index, or ndarray
516 1-dimensional array to take values from
517 key: : scalar
518 The value of this index at the position of the desired value,
519 otherwise the positional index of the desired value
521 Returns
522 -------
523 Any
524 The element of the series at the position indicated by the key
525 """
526 try:
527 k = com.values_from_object(key)
528 k = self._convert_scalar_indexer(k, kind="getitem")
529 indexer = self.get_loc(k)
530 return series.take([indexer])[0]
531 except (KeyError, TypeError):
532 pass
534 # we might be a positional inexer
535 return super().get_value(series, key)
537 @Appender(_index_shared_docs["where"])
538 def where(self, cond, other=None):
539 # TODO: Investigate an alternative implementation with
540 # 1. copy the underlying Categorical
541 # 2. setitem with `cond` and `other`
542 # 3. Rebuild CategoricalIndex.
543 if other is None:
544 other = self._na_value
545 values = np.where(cond, self.values, other)
546 cat = Categorical(values, dtype=self.dtype)
547 return self._shallow_copy(cat, **self._get_attributes_dict())
549 def reindex(self, target, method=None, level=None, limit=None, tolerance=None):
550 """
551 Create index with target's values (move/add/delete values as necessary)
553 Returns
554 -------
555 new_index : pd.Index
556 Resulting index
557 indexer : np.ndarray or None
558 Indices of output values in original index
560 """
561 if method is not None:
562 raise NotImplementedError(
563 "argument method is not implemented for CategoricalIndex.reindex"
564 )
565 if level is not None:
566 raise NotImplementedError(
567 "argument level is not implemented for CategoricalIndex.reindex"
568 )
569 if limit is not None:
570 raise NotImplementedError(
571 "argument limit is not implemented for CategoricalIndex.reindex"
572 )
574 target = ibase.ensure_index(target)
576 missing: List[int]
577 if self.equals(target):
578 indexer = None
579 missing = []
580 else:
581 indexer, missing = self.get_indexer_non_unique(np.array(target))
583 if len(self.codes) and indexer is not None:
584 new_target = self.take(indexer)
585 else:
586 new_target = target
588 # filling in missing if needed
589 if len(missing):
590 cats = self.categories.get_indexer(target)
592 if (cats == -1).any():
593 # coerce to a regular index here!
594 result = Index(np.array(self), name=self.name)
595 new_target, indexer, _ = result._reindex_non_unique(np.array(target))
596 else:
598 codes = new_target.codes.copy()
599 codes[indexer == -1] = cats[missing]
600 new_target = self._create_from_codes(codes)
602 # we always want to return an Index type here
603 # to be consistent with .reindex for other index types (e.g. they don't
604 # coerce based on the actual values, only on the dtype)
605 # unless we had an initial Categorical to begin with
606 # in which case we are going to conform to the passed Categorical
607 new_target = np.asarray(new_target)
608 if is_categorical_dtype(target):
609 new_target = target._shallow_copy(new_target, name=self.name)
610 else:
611 new_target = Index(new_target, name=self.name)
613 return new_target, indexer
615 def _reindex_non_unique(self, target):
616 """ reindex from a non-unique; which CategoricalIndex's are almost
617 always
618 """
619 new_target, indexer = self.reindex(target)
620 new_indexer = None
622 check = indexer == -1
623 if check.any():
624 new_indexer = np.arange(len(self.take(indexer)))
625 new_indexer[check] = -1
627 cats = self.categories.get_indexer(target)
628 if not (cats == -1).any():
629 # .reindex returns normal Index. Revert to CategoricalIndex if
630 # all targets are included in my categories
631 new_target = self._shallow_copy(new_target)
633 return new_target, indexer, new_indexer
635 @Appender(_index_shared_docs["get_indexer"] % _index_doc_kwargs)
636 def get_indexer(self, target, method=None, limit=None, tolerance=None):
637 method = missing.clean_reindex_fill_method(method)
638 target = ibase.ensure_index(target)
640 if self.is_unique and self.equals(target):
641 return np.arange(len(self), dtype="intp")
643 if method == "pad" or method == "backfill":
644 raise NotImplementedError(
645 "method='pad' and method='backfill' not "
646 "implemented yet for CategoricalIndex"
647 )
648 elif method == "nearest":
649 raise NotImplementedError(
650 "method='nearest' not implemented yet for CategoricalIndex"
651 )
653 if isinstance(target, CategoricalIndex) and self.values.is_dtype_equal(target):
654 if self.values.equals(target.values):
655 # we have the same codes
656 codes = target.codes
657 else:
658 codes = _recode_for_categories(
659 target.codes, target.categories, self.values.categories
660 )
661 else:
662 if isinstance(target, CategoricalIndex):
663 code_indexer = self.categories.get_indexer(target.categories)
664 codes = take_1d(code_indexer, target.codes, fill_value=-1)
665 else:
666 codes = self.categories.get_indexer(target)
668 indexer, _ = self._engine.get_indexer_non_unique(codes)
669 return ensure_platform_int(indexer)
671 @Appender(_index_shared_docs["get_indexer_non_unique"] % _index_doc_kwargs)
672 def get_indexer_non_unique(self, target):
673 target = ibase.ensure_index(target)
675 if isinstance(target, CategoricalIndex):
676 # Indexing on codes is more efficient if categories are the same:
677 if target.categories is self.categories:
678 target = target.codes
679 indexer, missing = self._engine.get_indexer_non_unique(target)
680 return ensure_platform_int(indexer), missing
681 target = target.values
683 codes = self.categories.get_indexer(target)
684 indexer, missing = self._engine.get_indexer_non_unique(codes)
685 return ensure_platform_int(indexer), missing
687 @Appender(_index_shared_docs["_convert_scalar_indexer"])
688 def _convert_scalar_indexer(self, key, kind=None):
689 if kind == "loc":
690 try:
691 return self.categories._convert_scalar_indexer(key, kind=kind)
692 except TypeError:
693 self._invalid_indexer("label", key)
694 return super()._convert_scalar_indexer(key, kind=kind)
696 @Appender(_index_shared_docs["_convert_list_indexer"])
697 def _convert_list_indexer(self, keyarr, kind=None):
698 # Return our indexer or raise if all of the values are not included in
699 # the categories
701 if self.categories._defer_to_indexing:
702 indexer = self.categories._convert_list_indexer(keyarr, kind=kind)
703 return Index(self.codes).get_indexer_for(indexer)
705 indexer = self.categories.get_indexer(np.asarray(keyarr))
706 if (indexer == -1).any():
707 raise KeyError(
708 "a list-indexer must only include values that are in the categories"
709 )
711 return self.get_indexer(keyarr)
713 @Appender(_index_shared_docs["_convert_arr_indexer"])
714 def _convert_arr_indexer(self, keyarr):
715 keyarr = com.asarray_tuplesafe(keyarr)
717 if self.categories._defer_to_indexing:
718 return keyarr
720 return self._shallow_copy(keyarr)
722 @Appender(_index_shared_docs["_convert_index_indexer"])
723 def _convert_index_indexer(self, keyarr):
724 return self._shallow_copy(keyarr)
726 def take_nd(self, *args, **kwargs):
727 """Alias for `take`"""
728 warnings.warn(
729 "CategoricalIndex.take_nd is deprecated, use CategoricalIndex.take instead",
730 FutureWarning,
731 stacklevel=2,
732 )
733 return self.take(*args, **kwargs)
735 @Appender(_index_shared_docs["_maybe_cast_slice_bound"])
736 def _maybe_cast_slice_bound(self, label, side, kind):
737 if kind == "loc":
738 return label
740 return super()._maybe_cast_slice_bound(label, side, kind)
742 def map(self, mapper):
743 """
744 Map values using input correspondence (a dict, Series, or function).
746 Maps the values (their categories, not the codes) of the index to new
747 categories. If the mapping correspondence is one-to-one the result is a
748 :class:`~pandas.CategoricalIndex` which has the same order property as
749 the original, otherwise an :class:`~pandas.Index` is returned.
751 If a `dict` or :class:`~pandas.Series` is used any unmapped category is
752 mapped to `NaN`. Note that if this happens an :class:`~pandas.Index`
753 will be returned.
755 Parameters
756 ----------
757 mapper : function, dict, or Series
758 Mapping correspondence.
760 Returns
761 -------
762 pandas.CategoricalIndex or pandas.Index
763 Mapped index.
765 See Also
766 --------
767 Index.map : Apply a mapping correspondence on an
768 :class:`~pandas.Index`.
769 Series.map : Apply a mapping correspondence on a
770 :class:`~pandas.Series`.
771 Series.apply : Apply more complex functions on a
772 :class:`~pandas.Series`.
774 Examples
775 --------
776 >>> idx = pd.CategoricalIndex(['a', 'b', 'c'])
777 >>> idx
778 CategoricalIndex(['a', 'b', 'c'], categories=['a', 'b', 'c'],
779 ordered=False, dtype='category')
780 >>> idx.map(lambda x: x.upper())
781 CategoricalIndex(['A', 'B', 'C'], categories=['A', 'B', 'C'],
782 ordered=False, dtype='category')
783 >>> idx.map({'a': 'first', 'b': 'second', 'c': 'third'})
784 CategoricalIndex(['first', 'second', 'third'], categories=['first',
785 'second', 'third'], ordered=False, dtype='category')
787 If the mapping is one-to-one the ordering of the categories is
788 preserved:
790 >>> idx = pd.CategoricalIndex(['a', 'b', 'c'], ordered=True)
791 >>> idx
792 CategoricalIndex(['a', 'b', 'c'], categories=['a', 'b', 'c'],
793 ordered=True, dtype='category')
794 >>> idx.map({'a': 3, 'b': 2, 'c': 1})
795 CategoricalIndex([3, 2, 1], categories=[3, 2, 1], ordered=True,
796 dtype='category')
798 If the mapping is not one-to-one an :class:`~pandas.Index` is returned:
800 >>> idx.map({'a': 'first', 'b': 'second', 'c': 'first'})
801 Index(['first', 'second', 'first'], dtype='object')
803 If a `dict` is used, all unmapped categories are mapped to `NaN` and
804 the result is an :class:`~pandas.Index`:
806 >>> idx.map({'a': 'first', 'b': 'second'})
807 Index(['first', 'second', nan], dtype='object')
808 """
809 return self._shallow_copy_with_infer(self.values.map(mapper))
811 def delete(self, loc):
812 """
813 Make new Index with passed location(-s) deleted
815 Returns
816 -------
817 new_index : Index
818 """
819 return self._create_from_codes(np.delete(self.codes, loc))
821 def insert(self, loc, item):
822 """
823 Make new Index inserting new item at location. Follows
824 Python list.append semantics for negative values
826 Parameters
827 ----------
828 loc : int
829 item : object
831 Returns
832 -------
833 new_index : Index
835 Raises
836 ------
837 ValueError if the item is not in the categories
839 """
840 code = self.categories.get_indexer([item])
841 if (code == -1) and not (is_scalar(item) and isna(item)):
842 raise TypeError(
843 "cannot insert an item into a CategoricalIndex "
844 "that is not already an existing category"
845 )
847 codes = self.codes
848 codes = np.concatenate((codes[:loc], code, codes[loc:]))
849 return self._create_from_codes(codes)
851 def _concat(self, to_concat, name):
852 # if calling index is category, don't check dtype of others
853 return CategoricalIndex._concat_same_dtype(self, to_concat, name)
855 def _concat_same_dtype(self, to_concat, name):
856 """
857 Concatenate to_concat which has the same class
858 ValueError if other is not in the categories
859 """
860 codes = np.concatenate([self._is_dtype_compat(c).codes for c in to_concat])
861 result = self._create_from_codes(codes, name=name)
862 # if name is None, _create_from_codes sets self.name
863 result.name = name
864 return result
866 def _delegate_property_get(self, name, *args, **kwargs):
867 """ method delegation to the ._values """
868 prop = getattr(self._values, name)
869 return prop # no wrapping for now
871 def _delegate_method(self, name, *args, **kwargs):
872 """ method delegation to the ._values """
873 method = getattr(self._values, name)
874 if "inplace" in kwargs:
875 raise ValueError("cannot use inplace with CategoricalIndex")
876 res = method(*args, **kwargs)
877 if is_scalar(res) or name in self._raw_inherit:
878 return res
879 return CategoricalIndex(res, name=self.name)
882CategoricalIndex._add_numeric_methods_add_sub_disabled()
883CategoricalIndex._add_numeric_methods_disabled()
884CategoricalIndex._add_logical_methods_disabled()