""" frozen (immutable) data structures to support MultiIndexing These are used for: - .names (FrozenList) - .levels & .codes (FrozenNDArray) """ import warnings import numpy as np from pandas.util._decorators import deprecate_kwarg from pandas.core.dtypes.cast import coerce_indexer_dtype from pandas.core.base import PandasObject from pandas.io.formats.printing import pprint_thing class FrozenList(PandasObject, list): """ Container that doesn't allow setting item *but* because it's technically non-hashable, will be used for lookups, appropriately, etc. """ # Side note: This has to be of type list. Otherwise, # it messes up PyTables type checks. def union(self, other): """ Returns a FrozenList with other concatenated to the end of self. Parameters ---------- other : array-like The array-like whose elements we are concatenating. Returns ------- diff : FrozenList The collection difference between self and other. """ if isinstance(other, tuple): other = list(other) return type(self)(super(FrozenList, self).__add__(other)) def difference(self, other): """ Returns a FrozenList with elements from other removed from self. Parameters ---------- other : array-like The array-like whose elements we are removing self. Returns ------- diff : FrozenList The collection difference between self and other. """ other = set(other) temp = [x for x in self if x not in other] return type(self)(temp) # TODO: Consider deprecating these in favor of `union` (xref gh-15506) __add__ = __iadd__ = union # Python 2 compat def __getslice__(self, i, j): return self.__class__(super(FrozenList, self).__getslice__(i, j)) def __getitem__(self, n): # Python 3 compat if isinstance(n, slice): return self.__class__(super(FrozenList, self).__getitem__(n)) return super(FrozenList, self).__getitem__(n) def __radd__(self, other): if isinstance(other, tuple): other = list(other) return self.__class__(other + list(self)) def __eq__(self, other): if isinstance(other, (tuple, FrozenList)): other = list(other) return super(FrozenList, self).__eq__(other) __req__ = __eq__ def __mul__(self, other): return self.__class__(super(FrozenList, self).__mul__(other)) __imul__ = __mul__ def __reduce__(self): return self.__class__, (list(self),) def __hash__(self): return hash(tuple(self)) def _disabled(self, *args, **kwargs): """This method will not function because object is immutable.""" raise TypeError("'%s' does not support mutable operations." % self.__class__.__name__) def __unicode__(self): return pprint_thing(self, quote_strings=True, escape_chars=('\t', '\r', '\n')) def __repr__(self): return "%s(%s)" % (self.__class__.__name__, str(self)) __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled pop = append = extend = remove = sort = insert = _disabled class FrozenNDArray(PandasObject, np.ndarray): # no __array_finalize__ for now because no metadata def __new__(cls, data, dtype=None, copy=False): warnings.warn("\nFrozenNDArray is deprecated and will be removed in a " "future version.\nPlease use `numpy.ndarray` instead.\n", FutureWarning, stacklevel=2) if copy is None: copy = not isinstance(data, FrozenNDArray) res = np.array(data, dtype=dtype, copy=copy).view(cls) return res def _disabled(self, *args, **kwargs): """This method will not function because object is immutable.""" raise TypeError("'%s' does not support mutable operations." % self.__class__) __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled put = itemset = fill = _disabled def _shallow_copy(self): return self.view() def values(self): """returns *copy* of underlying array""" arr = self.view(np.ndarray).copy() return arr def __unicode__(self): """ Return a string representation for this object. Invoked by unicode(df) in py2 only. Yields a Unicode String in both py2/py3. """ prepr = pprint_thing(self, escape_chars=('\t', '\r', '\n'), quote_strings=True) return "%s(%s, dtype='%s')" % (type(self).__name__, prepr, self.dtype) @deprecate_kwarg(old_arg_name="v", new_arg_name="value") def searchsorted(self, value, side="left", sorter=None): """ Find indices to insert `value` so as to maintain order. For full documentation, see `numpy.searchsorted` See Also -------- numpy.searchsorted : Equivalent function. """ # We are much more performant if the searched # indexer is the same type as the array. # # This doesn't matter for int64, but DOES # matter for smaller int dtypes. # # xref: https://github.com/numpy/numpy/issues/5370 try: value = self.dtype.type(value) except ValueError: pass return super(FrozenNDArray, self).searchsorted( value, side=side, sorter=sorter) def _ensure_frozen(array_like, categories, copy=False): array_like = coerce_indexer_dtype(array_like, categories) array_like = array_like.view(FrozenNDArray) if copy: array_like = array_like.copy() return array_like