From 2f5fbaad5c6d9b3364a3daac036d0ddc8ff31845 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 20 Nov 2020 15:57:23 -0800 Subject: [PATCH 1/2] BUG: IntervalArray.astype(categorical_dtype) losing ordered --- pandas/core/arrays/interval.py | 2 +- pandas/core/indexes/interval.py | 4 +--- pandas/tests/arrays/interval/test_astype.py | 23 ++++++++++++++++++++ pandas/tests/indexes/interval/test_astype.py | 8 ++++++- 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 pandas/tests/arrays/interval/test_astype.py diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index d007bb112c86c..2b719c717e624 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -702,7 +702,7 @@ def astype(self, dtype, copy=True): combined = _get_combined_data(new_left, new_right) return type(self)._simple_new(combined, closed=self.closed) elif is_categorical_dtype(dtype): - return Categorical(np.asarray(self)) + return Categorical(np.asarray(self), dtype=dtype) elif isinstance(dtype, StringDtype): return dtype.construct_array_type()._from_sequence(self, copy=False) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index b1b3c594512b1..01381789a68a9 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -373,9 +373,7 @@ def __reduce__(self): def astype(self, dtype, copy: bool = True): with rewrite_exception("IntervalArray", type(self).__name__): new_values = self._values.astype(dtype, copy=copy) - if is_interval_dtype(new_values.dtype): - return self._shallow_copy(new_values) - return Index.astype(self, dtype, copy=copy) + return Index(new_values, dtype=new_values.dtype, name=self.name) @property def inferred_type(self) -> str: diff --git a/pandas/tests/arrays/interval/test_astype.py b/pandas/tests/arrays/interval/test_astype.py new file mode 100644 index 0000000000000..e118e40196e43 --- /dev/null +++ b/pandas/tests/arrays/interval/test_astype.py @@ -0,0 +1,23 @@ +import pytest + +from pandas import Categorical, CategoricalDtype, Index, IntervalIndex +import pandas._testing as tm + + +class TestAstype: + @pytest.mark.parametrize("ordered", [True, False]) + def test_astype_categorical_retains_ordered(self, ordered): + index = IntervalIndex.from_breaks(range(5)) + arr = index._data + + dtype = CategoricalDtype(None, ordered=ordered) + + expected = Categorical(list(arr), ordered=ordered) + result = arr.astype(dtype) + assert result.ordered is ordered + tm.assert_categorical_equal(result, expected) + + # test IntervalIndex.astype while we're at it. + result = index.astype(dtype) + expected = Index(expected) + tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index c94af6c0d533e..7bf1ea7355b61 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -114,7 +114,13 @@ def test_subtype_integer_errors(self): # int64 -> uint64 fails with negative values index = interval_range(-10, 10) dtype = IntervalDtype("uint64") - with pytest.raises(ValueError): + + # Until we decide what the exception message _should_ be, we + # assert something that it should _not_ be. + # We should _not_ be getting a message suggesting that the -10 + # has been wrapped around to a large-positive integer + msg = "^(?!(left side of interval must be <= right side))" + with pytest.raises(ValueError, match=msg): index.astype(dtype) From 802da0eeacef2c15d5d2621ac60f5b834ced4eed Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 21 Nov 2020 13:34:47 -0800 Subject: [PATCH 2/2] whatsnew --- doc/source/whatsnew/v1.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index bc5229d4b4296..cda1efeeb1b2a 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -589,6 +589,7 @@ Interval - Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` where :class:`Interval` dtypes would be converted to object dtypes (:issue:`34871`) - Bug in :meth:`IntervalIndex.take` with negative indices and ``fill_value=None`` (:issue:`37330`) - Bug in :meth:`IntervalIndex.putmask` with datetime-like dtype incorrectly casting to object dtype (:issue:`37968`) +- Bug in :meth:`IntervalArray.astype` incorrectly dropping dtype information with a :class:`CategoricalDtype` object (:issue:`37984`) - Indexing