Skip to content

Remove FrozenNDArray #29335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 2 additions & 16 deletions pandas/compat/pickle_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
83 changes: 0 additions & 83 deletions pandas/core/indexes/frozen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
13 changes: 10 additions & 3 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
):
Expand All @@ -832,15 +839,15 @@ 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)._shallow_copy()
for lev, level_codes in zip(self._levels, codes)
)
else:
level = [self._get_level_number(l) for l in level]
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(
new_codes[lev_idx] = self._coerce(
level_codes, lev, copy=copy
)._shallow_copy()
new_codes = FrozenList(new_codes)
Expand Down
65 changes: 1 addition & 64 deletions pandas/tests/indexes/test_frozen.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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