diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index fa1669b1f3343..1ef075bb1a8c5 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -256,6 +256,7 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more. - Ability to read pickles containing :class:`Categorical` instances created with pre-0.16 version of pandas has been removed (:issue:`27538`) - Removed the previously deprecated ``reduce`` and ``broadcast`` arguments from :meth:`DataFrame.apply` (:issue:`18577`) - Removed the previously deprecated ``assert_raises_regex`` function in ``pandas.util.testing`` (:issue:`29174`) +- Removed the previously deprecated ``FrozenNDArray`` class in ``pandas.core.indexes.frozen`` (:issue:`29335`) - .. _whatsnew_1000.performance: diff --git a/pandas/compat/pickle_compat.py b/pandas/compat/pickle_compat.py index 3a36713ccdbda..19ab638fffc7f 100644 --- a/pandas/compat/pickle_compat.py +++ b/pandas/compat/pickle_compat.py @@ -105,22 +105,8 @@ def __new__(cls) -> "DataFrame": # type: ignore # If classes are moved, provide compat here. _class_locations_map = { ("pandas.core.sparse.array", "SparseArray"): ("pandas.core.arrays", "SparseArray"), - # 15477 - # - # TODO: When FrozenNDArray is removed, add - # the following lines for compat: - # - # ('pandas.core.base', 'FrozenNDArray'): - # ('numpy', 'ndarray'), - # ('pandas.core.indexes.frozen', 'FrozenNDArray'): - # ('numpy', 'ndarray'), - # - # Afterwards, remove the current entry - # for `pandas.core.base.FrozenNDArray`. - ("pandas.core.base", "FrozenNDArray"): ( - "pandas.core.indexes.frozen", - "FrozenNDArray", - ), + ("pandas.core.base", "FrozenNDArray"): ("numpy", "ndarray"), + ("pandas.core.indexes.frozen", "FrozenNDArray"): ("numpy", "ndarray"), ("pandas.core.base", "FrozenList"): ("pandas.core.indexes.frozen", "FrozenList"), # 10890 ("pandas.core.series", "TimeSeries"): ("pandas.core.series", "Series"), diff --git a/pandas/core/indexes/frozen.py b/pandas/core/indexes/frozen.py index a6c39d049c50c..ab8bf70b126a8 100644 --- a/pandas/core/indexes/frozen.py +++ b/pandas/core/indexes/frozen.py @@ -4,18 +4,8 @@ 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 @@ -112,76 +102,3 @@ def __repr__(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 __repr__(self): - """ - Return a string representation for this object. - """ - 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().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 diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index c99e42e48c346..12af19511b03c 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -13,6 +13,7 @@ from pandas.errors import PerformanceWarning, UnsortedIndexError from pandas.util._decorators import Appender, cache_readonly, deprecate_kwarg +from pandas.core.dtypes.cast import coerce_indexer_dtype from pandas.core.dtypes.common import ( ensure_int64, ensure_platform_int, @@ -40,7 +41,7 @@ _index_shared_docs, ensure_index, ) -from pandas.core.indexes.frozen import FrozenList, _ensure_frozen +from pandas.core.indexes.frozen import FrozenList import pandas.core.missing as missing from pandas.core.sorting import ( get_group_index, @@ -822,6 +823,12 @@ def labels(self): ) return self.codes + def _coerce(self, array_like, categories, copy=False): + array_like = coerce_indexer_dtype(array_like, categories) + if copy: + array_like = array_like.copy() + return array_like + def _set_codes( self, codes, level=None, copy=False, validate=True, verify_integrity=False ): @@ -832,7 +839,7 @@ def _set_codes( if level is None: new_codes = FrozenList( - _ensure_frozen(level_codes, lev, copy=copy)._shallow_copy() + self._coerce(level_codes, lev, copy=copy).view() for lev, level_codes in zip(self._levels, codes) ) else: @@ -840,9 +847,7 @@ def _set_codes( new_codes = list(self._codes) for lev_idx, level_codes in zip(level, codes): lev = self.levels[lev_idx] - new_codes[lev_idx] = _ensure_frozen( - level_codes, lev, copy=copy - )._shallow_copy() + new_codes[lev_idx] = self._coerce(level_codes, lev, copy=copy) new_codes = FrozenList(new_codes) if verify_integrity: diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 712feb7b8ef61..9f6b0325b7b33 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -1,11 +1,7 @@ -import warnings - -import numpy as np import pytest -from pandas.core.indexes.frozen import FrozenList, FrozenNDArray +from pandas.core.indexes.frozen import FrozenList from pandas.tests.test_base import CheckImmutable, CheckStringMixin -import pandas.util.testing as tm class TestFrozenList(CheckImmutable, CheckStringMixin): @@ -55,62 +51,3 @@ def test_tricky_container_to_bytes_raises(self): msg = "^'str' object cannot be interpreted as an integer$" with pytest.raises(TypeError, match=msg): bytes(self.unicode_container) - - -class TestFrozenNDArray(CheckImmutable, CheckStringMixin): - mutable_methods = ("put", "itemset", "fill") - - def setup_method(self, _): - self.lst = [3, 5, 7, -2] - self.klass = FrozenNDArray - - with warnings.catch_warnings(record=True): - warnings.simplefilter("ignore", FutureWarning) - - self.container = FrozenNDArray(self.lst) - self.unicode_container = FrozenNDArray(["\u05d0", "\u05d1", "c"]) - - def test_constructor_warns(self): - # see gh-9031 - with tm.assert_produces_warning(FutureWarning): - FrozenNDArray([1, 2, 3]) - - def test_tricky_container_to_bytes(self): - bytes(self.unicode_container) - - def test_shallow_copying(self): - original = self.container.copy() - assert isinstance(self.container.view(), FrozenNDArray) - assert not isinstance(self.container.view(np.ndarray), FrozenNDArray) - assert self.container.view() is not self.container - tm.assert_numpy_array_equal(self.container, original) - - # Shallow copy should be the same too - assert isinstance(self.container._shallow_copy(), FrozenNDArray) - - # setting should not be allowed - def testit(container): - container[0] = 16 - - self.check_mutable_error(testit, self.container) - - def test_values(self): - original = self.container.view(np.ndarray).copy() - n = original[0] + 15 - - vals = self.container.values() - tm.assert_numpy_array_equal(original, vals) - - assert original is not vals - vals[0] = n - - assert isinstance(self.container, FrozenNDArray) - tm.assert_numpy_array_equal(self.container.values(), original) - assert vals[0] == n - - def test_searchsorted(self): - expected = 2 - assert self.container.searchsorted(7) == expected - - with tm.assert_produces_warning(FutureWarning): - assert self.container.searchsorted(v=7) == expected