diff --git a/doc/redirects.csv b/doc/redirects.csv index 173e670e30f0e..90ddf6c4dc582 100644 --- a/doc/redirects.csv +++ b/doc/redirects.csv @@ -761,6 +761,7 @@ generated/pandas.IntervalIndex.mid,../reference/api/pandas.IntervalIndex.mid generated/pandas.IntervalIndex.overlaps,../reference/api/pandas.IntervalIndex.overlaps generated/pandas.IntervalIndex.right,../reference/api/pandas.IntervalIndex.right generated/pandas.IntervalIndex.set_closed,../reference/api/pandas.IntervalIndex.set_closed +generated/pandas.IntervalIndex.set_inclusive,../reference/api/pandas.IntervalIndex.set_inclusive generated/pandas.IntervalIndex.to_tuples,../reference/api/pandas.IntervalIndex.to_tuples generated/pandas.IntervalIndex.values,../reference/api/pandas.IntervalIndex.values generated/pandas.Interval.left,../reference/api/pandas.Interval.left diff --git a/doc/source/reference/arrays.rst b/doc/source/reference/arrays.rst index 0d8444841fcae..cd0ce581519a8 100644 --- a/doc/source/reference/arrays.rst +++ b/doc/source/reference/arrays.rst @@ -352,6 +352,7 @@ A collection of intervals may be stored in an :class:`arrays.IntervalArray`. arrays.IntervalArray.contains arrays.IntervalArray.overlaps arrays.IntervalArray.set_closed + arrays.IntervalArray.set_inclusive arrays.IntervalArray.to_tuples diff --git a/doc/source/reference/indexing.rst b/doc/source/reference/indexing.rst index 89a9a0a92ef08..589a339a1ca60 100644 --- a/doc/source/reference/indexing.rst +++ b/doc/source/reference/indexing.rst @@ -251,6 +251,7 @@ IntervalIndex components IntervalIndex.get_loc IntervalIndex.get_indexer IntervalIndex.set_closed + IntervalIndex.set_inclusive IntervalIndex.contains IntervalIndex.overlaps IntervalIndex.to_tuples diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 0b450fab53137..c9c523e7a2415 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -762,7 +762,7 @@ Other Deprecations - Deprecated the ``closed`` argument in :class:`IntervalIndex` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) - Deprecated the ``closed`` argument in :class:`IntervalDtype` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) - Deprecated the ``closed`` argument in :class:`.IntervalArray` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) -- Deprecated the ``closed`` argument in :class:`IntervalTree` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) +- Deprecated :meth:`.IntervalArray.set_closed` and :meth:`.IntervalIndex.set_closed` in favor of ``set_inclusive``; In a future version ``set_closed`` will get removed (:issue:`40245`) - Deprecated the ``closed`` argument in :class:`ArrowInterval` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) - Deprecated allowing ``unit="M"`` or ``unit="Y"`` in :class:`Timestamp` constructor with a non-round float value (:issue:`47267`) - Deprecated the ``display.column_space`` global configuration option (:issue:`7576`) diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 56aae3039f7d6..9685c4db51b67 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -156,6 +156,7 @@ contains overlaps set_closed +set_inclusive to_tuples %(extra_methods)s\ @@ -1385,9 +1386,11 @@ def closed(self) -> IntervalClosedType: Return an %(klass)s identical to the current one, but closed on the specified side. + .. deprecated:: 1.5.0 + Parameters ---------- - inclusive : {'left', 'right', 'both', 'neither'} + closed : {'left', 'right', 'both', 'neither'} Whether the intervals are closed on the left-side, right-side, both or neither. @@ -1420,8 +1423,58 @@ def closed(self) -> IntervalClosedType: ), } ) - @deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive") - def set_closed( + def set_closed(self: IntervalArrayT, closed: IntervalClosedType) -> IntervalArrayT: + warnings.warn( + "set_closed is deprecated and will be removed in a future version. " + "Use set_inclusive instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + return self.set_inclusive(closed) + + _interval_shared_docs["set_inclusive"] = textwrap.dedent( + """ + Return an %(klass)s identical to the current one, but closed on the + specified side. + + .. versionadded:: 1.5 + + Parameters + ---------- + inclusive : {'left', 'right', 'both', 'neither'} + Whether the intervals are closed on the left-side, right-side, both + or neither. + + Returns + ------- + new_index : %(klass)s + + %(examples)s\ + """ + ) + + @Appender( + _interval_shared_docs["set_inclusive"] + % { + "klass": "IntervalArray", + "examples": textwrap.dedent( + """\ + Examples + -------- + >>> index = pd.arrays.IntervalArray.from_breaks(range(4), "right") + >>> index + + [(0, 1], (1, 2], (2, 3]] + Length: 3, dtype: interval[int64, right] + >>> index.set_inclusive('both') + + [[0, 1], [1, 2], [2, 3]] + Length: 3, dtype: interval[int64, both] + """ + ), + } + ) + def set_inclusive( self: IntervalArrayT, inclusive: IntervalClosedType ) -> IntervalArrayT: if inclusive not in VALID_CLOSED: diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 5f48be921f7c6..b1f839daa694d 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -179,7 +179,7 @@ def _new_IntervalIndex(cls, d): ), } ) -@inherit_names(["set_closed", "to_tuples"], IntervalArray, wrap=True) +@inherit_names(["set_closed", "set_inclusive", "to_tuples"], IntervalArray, wrap=True) @inherit_names( [ "__array__", diff --git a/pandas/tests/arrays/interval/test_interval.py b/pandas/tests/arrays/interval/test_interval.py index 7ca86408a7f59..073e6b6119b14 100644 --- a/pandas/tests/arrays/interval/test_interval.py +++ b/pandas/tests/arrays/interval/test_interval.py @@ -60,12 +60,12 @@ def test_is_empty(self, constructor, left, right, closed): class TestMethods: - @pytest.mark.parametrize("new_closed", ["left", "right", "both", "neither"]) - def test_set_closed(self, closed, new_closed): + @pytest.mark.parametrize("new_inclusive", ["left", "right", "both", "neither"]) + def test_set_inclusive(self, closed, new_inclusive): # GH 21670 array = IntervalArray.from_breaks(range(10), inclusive=closed) - result = array.set_closed(new_closed) - expected = IntervalArray.from_breaks(range(10), inclusive=new_closed) + result = array.set_inclusive(new_inclusive) + expected = IntervalArray.from_breaks(range(10), inclusive=new_inclusive) tm.assert_extension_array_equal(result, expected) @pytest.mark.parametrize( @@ -134,10 +134,10 @@ def test_set_na(self, left_right_dtypes): tm.assert_extension_array_equal(result, expected) - def test_setitem_mismatched_closed(self): + def test_setitem_mismatched_inclusive(self): arr = IntervalArray.from_breaks(range(4), "right") orig = arr.copy() - other = arr.set_closed("both") + other = arr.set_inclusive("both") msg = "'value.inclusive' is 'both', expected 'right'" with pytest.raises(ValueError, match=msg): @@ -488,17 +488,8 @@ def test_from_arrays_deprecation(): IntervalArray.from_arrays([0, 1, 2], [1, 2, 3], closed="right") -def test_set_closed_deprecated_closed(): +def test_set_closed_deprecated(): # GH#40245 array = IntervalArray.from_breaks(range(10)) with tm.assert_produces_warning(FutureWarning): array.set_closed(closed="both") - - -def test_set_closed_both_provided_deprecation(): - # GH#40245 - array = IntervalArray.from_breaks(range(10)) - msg = "Can only specify 'closed' or 'inclusive', not both." - with pytest.raises(TypeError, match=msg): - with tm.assert_produces_warning(FutureWarning): - array.set_closed(inclusive="both", closed="both") diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index 90497780311de..5bf29093152d8 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -871,21 +871,21 @@ def test_nbytes(self): expected = 64 # 4 * 8 * 2 assert result == expected - @pytest.mark.parametrize("new_closed", ["left", "right", "both", "neither"]) - def test_set_closed(self, name, closed, new_closed): + @pytest.mark.parametrize("new_inclusive", ["left", "right", "both", "neither"]) + def test_set_inclusive(self, name, closed, new_inclusive): # GH 21670 index = interval_range(0, 5, inclusive=closed, name=name) - result = index.set_closed(new_closed) - expected = interval_range(0, 5, inclusive=new_closed, name=name) + result = index.set_inclusive(new_inclusive) + expected = interval_range(0, 5, inclusive=new_inclusive, name=name) tm.assert_index_equal(result, expected) @pytest.mark.parametrize("bad_inclusive", ["foo", 10, "LEFT", True, False]) - def test_set_closed_errors(self, bad_inclusive): + def test_set_inclusive_errors(self, bad_inclusive): # GH 21670 index = interval_range(0, 5) msg = f"invalid option for 'inclusive': {bad_inclusive}" with pytest.raises(ValueError, match=msg): - index.set_closed(bad_inclusive) + index.set_inclusive(bad_inclusive) def test_is_all_dates(self): # GH 23576