frozen.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. """
  2. frozen (immutable) data structures to support MultiIndexing
  3. These are used for:
  4. - .names (FrozenList)
  5. - .levels & .codes (FrozenNDArray)
  6. """
  7. import warnings
  8. import numpy as np
  9. from pandas.util._decorators import deprecate_kwarg
  10. from pandas.core.dtypes.cast import coerce_indexer_dtype
  11. from pandas.core.base import PandasObject
  12. from pandas.io.formats.printing import pprint_thing
  13. class FrozenList(PandasObject, list):
  14. """
  15. Container that doesn't allow setting item *but*
  16. because it's technically non-hashable, will be used
  17. for lookups, appropriately, etc.
  18. """
  19. # Side note: This has to be of type list. Otherwise,
  20. # it messes up PyTables type checks.
  21. def union(self, other):
  22. """
  23. Returns a FrozenList with other concatenated to the end of self.
  24. Parameters
  25. ----------
  26. other : array-like
  27. The array-like whose elements we are concatenating.
  28. Returns
  29. -------
  30. diff : FrozenList
  31. The collection difference between self and other.
  32. """
  33. if isinstance(other, tuple):
  34. other = list(other)
  35. return type(self)(super(FrozenList, self).__add__(other))
  36. def difference(self, other):
  37. """
  38. Returns a FrozenList with elements from other removed from self.
  39. Parameters
  40. ----------
  41. other : array-like
  42. The array-like whose elements we are removing self.
  43. Returns
  44. -------
  45. diff : FrozenList
  46. The collection difference between self and other.
  47. """
  48. other = set(other)
  49. temp = [x for x in self if x not in other]
  50. return type(self)(temp)
  51. # TODO: Consider deprecating these in favor of `union` (xref gh-15506)
  52. __add__ = __iadd__ = union
  53. # Python 2 compat
  54. def __getslice__(self, i, j):
  55. return self.__class__(super(FrozenList, self).__getslice__(i, j))
  56. def __getitem__(self, n):
  57. # Python 3 compat
  58. if isinstance(n, slice):
  59. return self.__class__(super(FrozenList, self).__getitem__(n))
  60. return super(FrozenList, self).__getitem__(n)
  61. def __radd__(self, other):
  62. if isinstance(other, tuple):
  63. other = list(other)
  64. return self.__class__(other + list(self))
  65. def __eq__(self, other):
  66. if isinstance(other, (tuple, FrozenList)):
  67. other = list(other)
  68. return super(FrozenList, self).__eq__(other)
  69. __req__ = __eq__
  70. def __mul__(self, other):
  71. return self.__class__(super(FrozenList, self).__mul__(other))
  72. __imul__ = __mul__
  73. def __reduce__(self):
  74. return self.__class__, (list(self),)
  75. def __hash__(self):
  76. return hash(tuple(self))
  77. def _disabled(self, *args, **kwargs):
  78. """This method will not function because object is immutable."""
  79. raise TypeError("'%s' does not support mutable operations." %
  80. self.__class__.__name__)
  81. def __unicode__(self):
  82. return pprint_thing(self, quote_strings=True,
  83. escape_chars=('\t', '\r', '\n'))
  84. def __repr__(self):
  85. return "%s(%s)" % (self.__class__.__name__,
  86. str(self))
  87. __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
  88. pop = append = extend = remove = sort = insert = _disabled
  89. class FrozenNDArray(PandasObject, np.ndarray):
  90. # no __array_finalize__ for now because no metadata
  91. def __new__(cls, data, dtype=None, copy=False):
  92. warnings.warn("\nFrozenNDArray is deprecated and will be removed in a "
  93. "future version.\nPlease use `numpy.ndarray` instead.\n",
  94. FutureWarning, stacklevel=2)
  95. if copy is None:
  96. copy = not isinstance(data, FrozenNDArray)
  97. res = np.array(data, dtype=dtype, copy=copy).view(cls)
  98. return res
  99. def _disabled(self, *args, **kwargs):
  100. """This method will not function because object is immutable."""
  101. raise TypeError("'%s' does not support mutable operations." %
  102. self.__class__)
  103. __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
  104. put = itemset = fill = _disabled
  105. def _shallow_copy(self):
  106. return self.view()
  107. def values(self):
  108. """returns *copy* of underlying array"""
  109. arr = self.view(np.ndarray).copy()
  110. return arr
  111. def __unicode__(self):
  112. """
  113. Return a string representation for this object.
  114. Invoked by unicode(df) in py2 only. Yields a Unicode String in both
  115. py2/py3.
  116. """
  117. prepr = pprint_thing(self, escape_chars=('\t', '\r', '\n'),
  118. quote_strings=True)
  119. return "%s(%s, dtype='%s')" % (type(self).__name__, prepr, self.dtype)
  120. @deprecate_kwarg(old_arg_name="v", new_arg_name="value")
  121. def searchsorted(self, value, side="left", sorter=None):
  122. """
  123. Find indices to insert `value` so as to maintain order.
  124. For full documentation, see `numpy.searchsorted`
  125. See Also
  126. --------
  127. numpy.searchsorted : Equivalent function.
  128. """
  129. # We are much more performant if the searched
  130. # indexer is the same type as the array.
  131. #
  132. # This doesn't matter for int64, but DOES
  133. # matter for smaller int dtypes.
  134. #
  135. # xref: https://github.com/numpy/numpy/issues/5370
  136. try:
  137. value = self.dtype.type(value)
  138. except ValueError:
  139. pass
  140. return super(FrozenNDArray, self).searchsorted(
  141. value, side=side, sorter=sorter)
  142. def _ensure_frozen(array_like, categories, copy=False):
  143. array_like = coerce_indexer_dtype(array_like, categories)
  144. array_like = array_like.view(FrozenNDArray)
  145. if copy:
  146. array_like = array_like.copy()
  147. return array_like