diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index ea6a04ac726b7..743a552b81ea2 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -831,6 +831,7 @@ Other deprecations - The default value ``ordered=None`` in :class:`~pandas.api.types.CategoricalDtype` has been deprecated in favor of ``ordered=False``. When converting between categorical types ``ordered=True`` must be explicitly passed in order to be preserved. (:issue:`26336`) - :meth:`Index.contains` is deprecated. Use ``key in index`` (``__contains__``) instead (:issue:`17753`). - :meth:`DataFrame.get_dtype_counts` is deprecated. (:issue:`18262`) +- :meth:`Categorical.ravel` will return a :class:`Categorical` instead of a ``np.ndarray`` (:issue:`27199`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 0762a607f20ae..75ea81d4e51db 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -908,6 +908,21 @@ def _formatting_values(self) -> np.ndarray: # Reshaping # ------------------------------------------------------------------------ + def ravel(self, order="C") -> ABCExtensionArray: + """ + Return a flattened view on this array. + + Parameters + ---------- + order : {None, 'C', 'F', 'A', 'K'}, default 'C' + + Notes + ----- + - Because ExtensionArrays are 1D-only, this is a no-op. + - The "order" argument is ignored, is for compatibility with NumPy. + """ + return self + @classmethod def _concat_same_type( cls, diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 9a4846c98bd22..05df4877bf98a 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1707,6 +1707,9 @@ def ravel(self, order='C'): ------- numpy.array """ + warn("Categorical.ravel will return a Categorical object instead " + "of an ndarray in a future version.", + FutureWarning, stacklevel=2) return np.array(self) def view(self): diff --git a/pandas/tests/extension/base/base.py b/pandas/tests/extension/base/base.py index b11603c0e185a..55cfbea479c47 100644 --- a/pandas/tests/extension/base/base.py +++ b/pandas/tests/extension/base/base.py @@ -2,6 +2,7 @@ class BaseExtensionTests: + assert_equal = staticmethod(tm.assert_equal) assert_series_equal = staticmethod(tm.assert_series_equal) assert_frame_equal = staticmethod(tm.assert_frame_equal) diff --git a/pandas/tests/extension/base/reshaping.py b/pandas/tests/extension/base/reshaping.py index ee22ffb3ccf97..4ea78a4239e6e 100644 --- a/pandas/tests/extension/base/reshaping.py +++ b/pandas/tests/extension/base/reshaping.py @@ -269,3 +269,12 @@ def test_unstack(self, data, index, obj): result = result.astype(object) self.assert_frame_equal(result, expected) + + def test_ravel(self, data): + # as long as EA is 1D-only, ravel is a no-op + result = data.ravel() + assert type(result) == type(data) + + # Check that we have a view, not a copy + result[0] = result[1] + assert data[0] == data[1] diff --git a/pandas/tests/extension/test_categorical.py b/pandas/tests/extension/test_categorical.py index 4cf9f78e1531d..046dcc1c74a03 100644 --- a/pandas/tests/extension/test_categorical.py +++ b/pandas/tests/extension/test_categorical.py @@ -22,6 +22,7 @@ from pandas import Categorical from pandas.api.types import CategoricalDtype from pandas.tests.extension import base +import pandas.util.testing as tm def make_data(): @@ -94,7 +95,11 @@ class TestConstructors(base.BaseConstructorsTests): class TestReshaping(base.BaseReshapingTests): - pass + + def test_ravel(self, data): + # GH#27199 Categorical.ravel returns self until after deprecation cycle + with tm.assert_produces_warning(FutureWarning): + data.ravel() class TestGetitem(base.BaseGetitemTests): diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py index 86ca3e230ddd5..8ce53270b7ba8 100644 --- a/pandas/tests/extension/test_sparse.py +++ b/pandas/tests/extension/test_sparse.py @@ -82,11 +82,14 @@ def data_for_grouping(request): class BaseSparseTests: - def _check_unsupported(self, data): if data.dtype == SparseDtype(int, 0): pytest.skip("Can't store nan in int array.") + @pytest.mark.xfail(reason="SparseArray does not support setitem") + def test_ravel(self, data): + super().test_ravel(data) + class TestDtype(BaseSparseTests, base.BaseDtypeTests):