From a0e56291cb650e3a629680feb396765dc1179cff Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Mon, 14 Mar 2022 00:19:10 +0800 Subject: [PATCH 01/26] Update v1.5.0.rst --- doc/source/whatsnew/v1.5.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 8dac952874f89..eaea367f25d01 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -297,7 +297,7 @@ Other Deprecations - Deprecated the ``warn`` parameter in :func:`infer_freq` (:issue:`45947`) - Deprecated allowing non-keyword arguments in :meth:`ExtensionArray.argsort` (:issue:`46134`) - Deprecated treating all-bool ``object``-dtype columns as bool-like in :meth:`DataFrame.any` and :meth:`DataFrame.all` with ``bool_only=True``, explicitly cast to bool instead (:issue:`46188`) -- +- Deprecated the ``closed`` argument in :meth:`interval_range` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) .. --------------------------------------------------------------------------- .. _whatsnew_150.performance: From b7c833c7f18c06a1373c35e886baebfa9ca25b6a Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Mon, 14 Mar 2022 00:21:06 +0800 Subject: [PATCH 02/26] Update interval.py --- pandas/core/indexes/interval.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index c1d7eb972e1f4..1bc7578dadd46 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -954,7 +954,7 @@ def interval_range( periods=None, freq=None, name: Hashable = None, - closed: IntervalClosedType = "right", + inclusive: IntervalClosedType = "right", ) -> IntervalIndex: """ Return a fixed frequency IntervalIndex. @@ -973,7 +973,7 @@ def interval_range( for numeric and 'D' for datetime-like. name : str, default None Name of the resulting IntervalIndex. - closed : {'left', 'right', 'both', 'neither'}, default 'right' + inclusive : {'left', 'right', 'both', 'neither'}, default 'right' Whether the intervals are closed on the left-side, right-side, both or neither. @@ -1035,10 +1035,10 @@ def interval_range( IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]], dtype='interval[float64, right]') - The ``closed`` parameter specifies which endpoints of the individual + The ``inclusive`` parameter specifies which endpoints of the individual intervals within the ``IntervalIndex`` are closed. - >>> pd.interval_range(end=5, periods=4, closed='both') + >>> pd.interval_range(end=5, periods=4, inclusive='both') IntervalIndex([[1, 2], [2, 3], [3, 4], [4, 5]], dtype='interval[int64, both]') """ @@ -1118,4 +1118,4 @@ def interval_range( else: breaks = timedelta_range(start=start, end=end, periods=periods, freq=freq) - return IntervalIndex.from_breaks(breaks, name=name, closed=closed) + return IntervalIndex.from_breaks(breaks, name=name, closed=inclusive) From 80120b7ab4fc233c398bfa3f2ee7e2685964d659 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 14 Mar 2022 00:22:17 +0800 Subject: [PATCH 03/26] pre commit --- pandas/tests/indexes/interval/test_astype.py | 7 +++--- .../tests/indexes/interval/test_interval.py | 12 +++++----- .../indexes/interval/test_interval_range.py | 24 +++++++++---------- pandas/tests/indexes/interval/test_setops.py | 2 +- pandas/tests/indexing/test_coercion.py | 2 +- pandas/tests/indexing/test_loc.py | 4 ++-- .../util/test_assert_interval_array_equal.py | 7 +++--- 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index c253a745ef5a2..d9abc323d1442 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -133,7 +133,7 @@ class TestFloatSubtype(AstypeTests): """Tests specific to IntervalIndex with float subtype""" indexes = [ - interval_range(-10.0, 10.0, closed="neither"), + interval_range(-10.0, 10.0, inclusive="neither"), IntervalIndex.from_arrays( [-1.5, np.nan, 0.0, 0.0, 1.5], [-0.5, np.nan, 1.0, 1.0, 3.0], closed="both" ), @@ -161,6 +161,7 @@ def test_subtype_integer(self, subtype): @pytest.mark.parametrize("subtype", ["int64", "uint64"]) def test_subtype_integer_with_non_integer_borders(self, subtype): index = interval_range(0.0, 3.0, freq=0.25) + dtype = IntervalDtype(subtype, "right") result = index.astype(dtype) expected = IntervalIndex.from_arrays( @@ -191,10 +192,10 @@ class TestDatetimelikeSubtype(AstypeTests): """Tests specific to IntervalIndex with datetime-like subtype""" indexes = [ - interval_range(Timestamp("2018-01-01"), periods=10, closed="neither"), + interval_range(Timestamp("2018-01-01"), periods=10, inclusive="neither"), interval_range(Timestamp("2018-01-01"), periods=10).insert(2, NaT), interval_range(Timestamp("2018-01-01", tz="US/Eastern"), periods=10), - interval_range(Timedelta("0 days"), periods=10, closed="both"), + interval_range(Timedelta("0 days"), periods=10, inclusive="both"), interval_range(Timedelta("0 days"), periods=10).insert(2, NaT), ] diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index 37c13c37d070b..8880cab2ce29b 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -167,10 +167,10 @@ def test_delete(self, closed): @pytest.mark.parametrize( "data", [ - interval_range(0, periods=10, closed="neither"), - interval_range(1.7, periods=8, freq=2.5, closed="both"), - interval_range(Timestamp("20170101"), periods=12, closed="left"), - interval_range(Timedelta("1 day"), periods=6, closed="right"), + interval_range(0, periods=10, inclusive="neither"), + interval_range(1.7, periods=8, freq=2.5, inclusive="both"), + interval_range(Timestamp("20170101"), periods=12, inclusive="left"), + interval_range(Timedelta("1 day"), periods=6, inclusive="right"), ], ) def test_insert(self, data): @@ -868,9 +868,9 @@ def test_nbytes(self): @pytest.mark.parametrize("new_closed", ["left", "right", "both", "neither"]) def test_set_closed(self, name, closed, new_closed): # GH 21670 - index = interval_range(0, 5, closed=closed, name=name) + index = interval_range(0, 5, inclusive=closed, name=name) result = index.set_closed(new_closed) - expected = interval_range(0, 5, closed=new_closed, name=name) + expected = interval_range(0, 5, inclusive=new_closed, name=name) tm.assert_index_equal(result, expected) @pytest.mark.parametrize("bad_closed", ["foo", 10, "LEFT", True, False]) diff --git a/pandas/tests/indexes/interval/test_interval_range.py b/pandas/tests/indexes/interval/test_interval_range.py index 2f28c33a3bbc6..e0af6047dbae5 100644 --- a/pandas/tests/indexes/interval/test_interval_range.py +++ b/pandas/tests/indexes/interval/test_interval_range.py @@ -34,25 +34,25 @@ def test_constructor_numeric(self, closed, name, freq, periods): # defined from start/end/freq result = interval_range( - start=start, end=end, freq=freq, name=name, closed=closed + start=start, end=end, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from start/periods/freq result = interval_range( - start=start, periods=periods, freq=freq, name=name, closed=closed + start=start, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from end/periods/freq result = interval_range( - end=end, periods=periods, freq=freq, name=name, closed=closed + end=end, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # GH 20976: linspace behavior defined from start/end/periods result = interval_range( - start=start, end=end, periods=periods, name=name, closed=closed + start=start, end=end, periods=periods, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) @@ -67,19 +67,19 @@ def test_constructor_timestamp(self, closed, name, freq, periods, tz): # defined from start/end/freq result = interval_range( - start=start, end=end, freq=freq, name=name, closed=closed + start=start, end=end, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from start/periods/freq result = interval_range( - start=start, periods=periods, freq=freq, name=name, closed=closed + start=start, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from end/periods/freq result = interval_range( - end=end, periods=periods, freq=freq, name=name, closed=closed + end=end, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) @@ -88,7 +88,7 @@ def test_constructor_timestamp(self, closed, name, freq, periods, tz): # matches expected only for non-anchored offsets and tz naive # (anchored/DST transitions cause unequal spacing in expected) result = interval_range( - start=start, end=end, periods=periods, name=name, closed=closed + start=start, end=end, periods=periods, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) @@ -102,25 +102,25 @@ def test_constructor_timedelta(self, closed, name, freq, periods): # defined from start/end/freq result = interval_range( - start=start, end=end, freq=freq, name=name, closed=closed + start=start, end=end, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from start/periods/freq result = interval_range( - start=start, periods=periods, freq=freq, name=name, closed=closed + start=start, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # defined from end/periods/freq result = interval_range( - end=end, periods=periods, freq=freq, name=name, closed=closed + end=end, periods=periods, freq=freq, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) # GH 20976: linspace behavior defined from start/end/periods result = interval_range( - start=start, end=end, periods=periods, name=name, closed=closed + start=start, end=end, periods=periods, name=name, inclusive=closed ) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/interval/test_setops.py b/pandas/tests/indexes/interval/test_setops.py index 059b0b75f4190..51a1d36398aa4 100644 --- a/pandas/tests/indexes/interval/test_setops.py +++ b/pandas/tests/indexes/interval/test_setops.py @@ -194,7 +194,7 @@ def test_set_incompatible_types(self, closed, op_name, sort): tm.assert_index_equal(result, expected) # GH 19016: incompatible dtypes -> cast to object - other = interval_range(Timestamp("20180101"), periods=9, closed=closed) + other = interval_range(Timestamp("20180101"), periods=9, inclusive=closed) expected = getattr(index.astype(object), op_name)(other, sort=sort) if op_name == "difference": expected = index diff --git a/pandas/tests/indexing/test_coercion.py b/pandas/tests/indexing/test_coercion.py index 2d54a9ba370ca..4504c55698a9a 100644 --- a/pandas/tests/indexing/test_coercion.py +++ b/pandas/tests/indexing/test_coercion.py @@ -709,7 +709,7 @@ def test_fillna_datetime64tz(self, index_or_series, fill_val, fill_dtype): ], ) def test_fillna_interval(self, index_or_series, fill_val): - ii = pd.interval_range(1.0, 5.0, closed="right").insert(1, np.nan) + ii = pd.interval_range(1.0, 5.0, inclusive="right").insert(1, np.nan) assert isinstance(ii.dtype, pd.IntervalDtype) obj = index_or_series(ii) diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 4702b5e5c4504..3f8e4401808b7 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1508,12 +1508,12 @@ def test_loc_getitem_interval_index(self): def test_loc_getitem_interval_index2(self): # GH#19977 - index = pd.interval_range(start=0, periods=3, closed="both") + index = pd.interval_range(start=0, periods=3, inclusive="both") df = DataFrame( [[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=index, columns=["A", "B", "C"] ) - index_exp = pd.interval_range(start=0, periods=2, freq=1, closed="both") + index_exp = pd.interval_range(start=0, periods=2, freq=1, inclusive="both") expected = Series([1, 4], index=index_exp, name="A") result = df.loc[1, "A"] tm.assert_series_equal(result, expected) diff --git a/pandas/tests/util/test_assert_interval_array_equal.py b/pandas/tests/util/test_assert_interval_array_equal.py index 8cc4ade3d7e95..4931aef26372a 100644 --- a/pandas/tests/util/test_assert_interval_array_equal.py +++ b/pandas/tests/util/test_assert_interval_array_equal.py @@ -9,7 +9,7 @@ [ {"start": 0, "periods": 4}, {"start": 1, "periods": 5}, - {"start": 5, "end": 10, "closed": "left"}, + {"start": 5, "end": 10, "inclusive": "left"}, ], ) def test_interval_array_equal(kwargs): @@ -19,8 +19,9 @@ def test_interval_array_equal(kwargs): def test_interval_array_equal_closed_mismatch(): kwargs = {"start": 0, "periods": 5} - arr1 = interval_range(closed="left", **kwargs).values - arr2 = interval_range(closed="right", **kwargs).values + arr1 = interval_range(inclusive="left", **kwargs).values + arr1 = interval_range(inclusive="left", **kwargs).values + arr2 = interval_range(inclusive="right", **kwargs).values msg = """\ IntervalArray are different From 717cae3c4dfe19b0505095be762f8b1dbdbba0b7 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 14 Mar 2022 00:22:47 +0800 Subject: [PATCH 04/26] pre commit --- pandas/tests/indexes/interval/test_astype.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index d9abc323d1442..7f5ef0cdd3bf4 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -161,7 +161,6 @@ def test_subtype_integer(self, subtype): @pytest.mark.parametrize("subtype", ["int64", "uint64"]) def test_subtype_integer_with_non_integer_borders(self, subtype): index = interval_range(0.0, 3.0, freq=0.25) - dtype = IntervalDtype(subtype, "right") result = index.astype(dtype) expected = IntervalIndex.from_arrays( From f412c0274a16f672732223fb91410d389a3690f0 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 14 Mar 2022 00:24:09 +0800 Subject: [PATCH 05/26] pre commit --- pandas/tests/util/test_assert_interval_array_equal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/util/test_assert_interval_array_equal.py b/pandas/tests/util/test_assert_interval_array_equal.py index 4931aef26372a..243f357d7298c 100644 --- a/pandas/tests/util/test_assert_interval_array_equal.py +++ b/pandas/tests/util/test_assert_interval_array_equal.py @@ -20,7 +20,6 @@ def test_interval_array_equal(kwargs): def test_interval_array_equal_closed_mismatch(): kwargs = {"start": 0, "periods": 5} arr1 = interval_range(inclusive="left", **kwargs).values - arr1 = interval_range(inclusive="left", **kwargs).values arr2 = interval_range(inclusive="right", **kwargs).values msg = """\ From 1457eec5c33e8833f6534ca4c982f8992d4067fd Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 14 Mar 2022 00:42:28 +0800 Subject: [PATCH 06/26] doctest --- pandas/core/indexes/interval.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 1bc7578dadd46..902183f046c7a 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -443,7 +443,7 @@ def is_overlapping(self) -> bool: Intervals that share closed endpoints overlap: - >>> index = pd.interval_range(0, 3, closed='both') + >>> index = pd.interval_range(0, 3, inclusive='both') >>> index IntervalIndex([[0, 1], [1, 2], [2, 3]], dtype='interval[int64, both]') @@ -452,7 +452,7 @@ def is_overlapping(self) -> bool: Intervals that only have an open endpoint in common do not overlap: - >>> index = pd.interval_range(0, 3, closed='left') + >>> index = pd.interval_range(0, 3, inclusive='left') >>> index IntervalIndex([[0, 1), [1, 2), [2, 3)], dtype='interval[int64, left]') From b6bc36130aa37bcd1e39a8f268d0d53951178c8d Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 14 Mar 2022 09:25:23 +0800 Subject: [PATCH 07/26] doc --- doc/source/user_guide/advanced.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index b8df21ab5a5b4..3062de7fcf172 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,9 +1087,9 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, closed="both") + pd.interval_range(start=0, end=4, inclusive="both") - pd.interval_range(start=0, end=4, closed="neither") + pd.interval_range(start=0, end=4, inclusive="neither") Specifying ``start``, ``end``, and ``periods`` will generate a range of evenly spaced intervals from ``start`` to ``end`` inclusively, with ``periods`` number of elements From 0d903a405525bbd975cac967357ab9630465af94 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Sat, 19 Mar 2022 22:35:36 +0800 Subject: [PATCH 08/26] resolve conflict --- doc/source/whatsnew/v1.5.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 880e6969f97cc..b66b50876c26b 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -341,7 +341,7 @@ Other Deprecations - Deprecated the ``warn`` parameter in :func:`infer_freq` (:issue:`45947`) - Deprecated allowing non-keyword arguments in :meth:`ExtensionArray.argsort` (:issue:`46134`) - Deprecated treating all-bool ``object``-dtype columns as bool-like in :meth:`DataFrame.any` and :meth:`DataFrame.all` with ``bool_only=True``, explicitly cast to bool instead (:issue:`46188`) -- Deprecated behavior of method :meth:`DataFrame.quantile`, attribute ``numeric_only`` will default False. Including datetime/timedelta columns in the result (:issue:`7308`) +- Deprecated behavior of method :meth:`DataFrame.quantile`, attribute ``numeric_only`` will default False. Including datetime/timedelta columns in the result (:issue:`7308`). - Deprecated the ``closed`` argument in :meth:`interval_range` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) - From 3a9fd9e84b8601b14fb28266457d35b8efb73520 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Sat, 19 Mar 2022 22:38:50 +0800 Subject: [PATCH 09/26] add deprecated --- pandas/core/indexes/interval.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 902183f046c7a..498032d0b1828 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -973,10 +973,18 @@ def interval_range( for numeric and 'D' for datetime-like. name : str, default None Name of the resulting IntervalIndex. - inclusive : {'left', 'right', 'both', 'neither'}, default 'right' + closed : {'left', 'right', 'both', 'neither'}, default 'right' Whether the intervals are closed on the left-side, right-side, both or neither. + .. deprecated:: 1.5.0 + Argument `closed` has been deprecated to standardize boundary inputs. + Use `inclusive` instead, to set each bound as closed or open. + inclusive : {"both", "neither", "left", "right"}, default "both" + Include boundaries; Whether to set each bound as closed or open. + + .. versionadded:: 1.5.0 + Returns ------- IntervalIndex From 25527f739a6c9c2bb05496ce8d7f122671b31558 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Sun, 20 Mar 2022 22:53:38 +0800 Subject: [PATCH 10/26] add message --- pandas/core/indexes/interval.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 498032d0b1828..ceb12379786fd 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -11,6 +11,7 @@ Hashable, Literal, ) +import warnings import numpy as np @@ -954,7 +955,8 @@ def interval_range( periods=None, freq=None, name: Hashable = None, - inclusive: IntervalClosedType = "right", + closed: lib.NoDefault = lib.no_default, + inclusive: str | None = None, ) -> IntervalIndex: """ Return a fixed frequency IntervalIndex. @@ -1050,6 +1052,27 @@ def interval_range( IntervalIndex([[1, 2], [2, 3], [3, 4], [4, 5]], dtype='interval[int64, both]') """ + if inclusive is not None and not isinstance(closed, lib.NoDefault): + raise ValueError( + "Deprecated argument `closed` cannot be passed " + "if argument `inclusive` is not None" + ) + elif not isinstance(closed, lib.NoDefault): + warnings.warn( + "Argument `closed` is deprecated in favor of `inclusive`.", + FutureWarning, + stacklevel=2, + ) + if closed is None: + inclusive = "both" + elif closed in ("both", "neither", "left", "right"): + inclusive = closed + else: + raise ValueError( + "Argument `closed` has to be either 'both', 'neither', 'left', 'right'," + "or 'both'" + ) + start = maybe_box_datetimelike(start) end = maybe_box_datetimelike(end) endpoint = start if start is not None else end From 1c41aa01b54184975ee280bbb932f01a6b7e045c Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 21 Mar 2022 00:19:04 +0800 Subject: [PATCH 11/26] test --- pandas/core/indexes/interval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index ceb12379786fd..8aaf796db31e4 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -956,7 +956,7 @@ def interval_range( freq=None, name: Hashable = None, closed: lib.NoDefault = lib.no_default, - inclusive: str | None = None, + inclusive: str = "both", ) -> IntervalIndex: """ Return a fixed frequency IntervalIndex. From 6a34ed14701552a13b4b561645fe5d95812e2b93 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Wed, 23 Mar 2022 00:31:35 +0800 Subject: [PATCH 12/26] Update interval.py --- pandas/core/indexes/interval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 8aaf796db31e4..640c4d4f6b228 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1064,7 +1064,7 @@ def interval_range( stacklevel=2, ) if closed is None: - inclusive = "both" + inclusive = "right" elif closed in ("both", "neither", "left", "right"): inclusive = closed else: From 9e3319de6639592e9a1d8635cf74a0622cad4957 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Wed, 23 Mar 2022 23:07:56 +0800 Subject: [PATCH 13/26] test --- pandas/core/indexes/interval.py | 2 +- pandas/tests/frame/methods/test_round.py | 2 +- pandas/tests/groupby/aggregate/test_cython.py | 2 +- pandas/tests/indexes/interval/test_astype.py | 4 ++-- pandas/tests/indexes/interval/test_interval_range.py | 6 ++++-- pandas/tests/io/excel/test_writers.py | 5 ++++- pandas/tests/series/indexing/test_setitem.py | 4 ++-- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 640c4d4f6b228..8aaf796db31e4 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1064,7 +1064,7 @@ def interval_range( stacklevel=2, ) if closed is None: - inclusive = "right" + inclusive = "both" elif closed in ("both", "neither", "left", "right"): inclusive = closed else: diff --git a/pandas/tests/frame/methods/test_round.py b/pandas/tests/frame/methods/test_round.py index dd9206940bcd6..77cadfff55e2f 100644 --- a/pandas/tests/frame/methods/test_round.py +++ b/pandas/tests/frame/methods/test_round.py @@ -210,7 +210,7 @@ def test_round_nonunique_categorical(self): def test_round_interval_category_columns(self): # GH#30063 - columns = pd.CategoricalIndex(pd.interval_range(0, 2)) + columns = pd.CategoricalIndex(pd.interval_range(0, 2, inclusive="right")) df = DataFrame([[0.66, 1.1], [0.3, 0.25]], columns=columns) result = df.round() diff --git a/pandas/tests/groupby/aggregate/test_cython.py b/pandas/tests/groupby/aggregate/test_cython.py index 96009be5d12e3..9631de7833cf4 100644 --- a/pandas/tests/groupby/aggregate/test_cython.py +++ b/pandas/tests/groupby/aggregate/test_cython.py @@ -214,7 +214,7 @@ def test_cython_agg_empty_buckets_nanops(observed): result = df.groupby(pd.cut(df["a"], grps), observed=observed)._cython_agg_general( "add", alt=None, numeric_only=True ) - intervals = pd.interval_range(0, 20, freq=5) + intervals = pd.interval_range(0, 20, freq=5, inclusive="right") expected = DataFrame( {"a": [0, 0, 36, 0]}, index=pd.CategoricalIndex(intervals, name="a", ordered=True), diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index 7f5ef0cdd3bf4..4cdbe2bbcf12b 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -117,7 +117,7 @@ def test_subtype_integer(self, subtype_start, subtype_end): @pytest.mark.xfail(reason="GH#15832") def test_subtype_integer_errors(self): # int64 -> uint64 fails with negative values - index = interval_range(-10, 10) + index = interval_range(-10, 10, inclusive="right") dtype = IntervalDtype("uint64", "right") # Until we decide what the exception message _should_ be, we @@ -170,7 +170,7 @@ def test_subtype_integer_with_non_integer_borders(self, subtype): def test_subtype_integer_errors(self): # float64 -> uint64 fails with negative values - index = interval_range(-10.0, 10.0) + index = interval_range(-10.0, 10.0, inclusive="right") dtype = IntervalDtype("uint64", "right") msg = re.escape( "Cannot convert interval[float64, right] to interval[uint64, right]; " diff --git a/pandas/tests/indexes/interval/test_interval_range.py b/pandas/tests/indexes/interval/test_interval_range.py index e0af6047dbae5..602fc35218520 100644 --- a/pandas/tests/indexes/interval/test_interval_range.py +++ b/pandas/tests/indexes/interval/test_interval_range.py @@ -163,7 +163,9 @@ def test_no_invalid_float_truncation(self, start, end, freq): breaks = [0.5, 2.0, 3.5, 5.0, 6.5] expected = IntervalIndex.from_breaks(breaks) - result = interval_range(start=start, end=end, periods=4, freq=freq) + result = interval_range( + start=start, end=end, periods=4, freq=freq, inclusive="right" + ) tm.assert_index_equal(result, expected) @pytest.mark.parametrize( @@ -184,7 +186,7 @@ def test_no_invalid_float_truncation(self, start, end, freq): def test_linspace_dst_transition(self, start, mid, end): # GH 20976: linspace behavior defined from start/end/periods # accounts for the hour gained/lost during DST transition - result = interval_range(start=start, end=end, periods=2) + result = interval_range(start=start, end=end, periods=2, inclusive="right") expected = IntervalIndex.from_breaks([start, mid, end]) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 06c106ed22329..20ed24b00d2e3 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -282,7 +282,10 @@ def test_multiindex_interval_datetimes(self, ext): [ range(4), pd.interval_range( - start=pd.Timestamp("2020-01-01"), periods=4, freq="6M" + start=pd.Timestamp("2020-01-01"), + periods=4, + freq="6M", + inclusive="right", ), ] ) diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 992f67c2affc6..1e84a05e2ae97 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -779,7 +779,7 @@ def test_index_putmask(self, obj, key, expected, val): pytest.param( # GH#45568 setting a valid NA value into IntervalDtype[int] should # cast to IntervalDtype[float] - Series(interval_range(1, 5)), + Series(interval_range(1, 5, inclusive="right")), Series( [Interval(1, 2), np.nan, Interval(3, 4), Interval(4, 5)], dtype="interval[float64]", @@ -1356,7 +1356,7 @@ class TestPeriodIntervalCoercion(CoercionTest): @pytest.fixture( params=[ period_range("2016-01-01", periods=3, freq="D"), - interval_range(1, 5), + interval_range(1, 5, inclusive="right"), ] ) def obj(self, request): From 888fb73c15e53a5bbdb658edc38205b358ac7523 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Wed, 23 Mar 2022 23:23:32 +0800 Subject: [PATCH 14/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 3062de7fcf172..d2ac93975518f 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="both") + pd.interval_range(start=0, end=4, inclusive="bot") pd.interval_range(start=0, end=4, inclusive="neither") From c9444af336f9fedc98cb9dfd34783af9220fb204 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Wed, 23 Mar 2022 23:23:53 +0800 Subject: [PATCH 15/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index d2ac93975518f..3062de7fcf172 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="bot") + pd.interval_range(start=0, end=4, inclusive="both") pd.interval_range(start=0, end=4, inclusive="neither") From 564345c0a6baf3a9e4cb9008186ed51913d2ea25 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Thu, 24 Mar 2022 01:09:51 +0800 Subject: [PATCH 16/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 3062de7fcf172..d2ac93975518f 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="both") + pd.interval_range(start=0, end=4, inclusive="bot") pd.interval_range(start=0, end=4, inclusive="neither") From 28c07ab39d1b29e92c9dc401f0c7622665cf0449 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Thu, 24 Mar 2022 01:10:06 +0800 Subject: [PATCH 17/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index d2ac93975518f..3062de7fcf172 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="bot") + pd.interval_range(start=0, end=4, inclusive="both") pd.interval_range(start=0, end=4, inclusive="neither") From fbcd8c9ac1dba6abd5be7ad5492d7898bf0e0ab7 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Thu, 24 Mar 2022 08:51:32 +0800 Subject: [PATCH 18/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 3062de7fcf172..562590b92565a 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="both") + pd.interval_range(start=0, end=4, inclusive="both ") pd.interval_range(start=0, end=4, inclusive="neither") From be489ffcc617b8e17b65883ef2dac5c752e5c2b2 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Thu, 24 Mar 2022 08:51:49 +0800 Subject: [PATCH 19/26] Update advanced.rst --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 562590b92565a..3062de7fcf172 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1087,7 +1087,7 @@ are closed on. Intervals are closed on the right side by default. .. ipython:: python - pd.interval_range(start=0, end=4, inclusive="both ") + pd.interval_range(start=0, end=4, inclusive="both") pd.interval_range(start=0, end=4, inclusive="neither") From 7a976824a1d5540ec4a387260f516253fdf1fac5 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Thu, 24 Mar 2022 10:06:58 +0800 Subject: [PATCH 20/26] pre commit --- pandas/core/indexes/interval.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 8aaf796db31e4..27dfd7c33a9c0 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -161,7 +161,7 @@ def _new_IntervalIndex(cls, d): A new ``IntervalIndex`` is typically constructed using :func:`interval_range`: - >>> pd.interval_range(start=0, end=5) + >>> pd.interval_range(start=0, end=5, inclusive="right") IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]') @@ -1009,14 +1009,14 @@ def interval_range( -------- Numeric ``start`` and ``end`` is supported. - >>> pd.interval_range(start=0, end=5) + >>> pd.interval_range(start=0, end=5, inclusive="right") IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]') Additionally, datetime-like input is also supported. >>> pd.interval_range(start=pd.Timestamp('2017-01-01'), - ... end=pd.Timestamp('2017-01-04')) + ... end=pd.Timestamp('2017-01-04'), inclusive="right") IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03], (2017-01-03, 2017-01-04]], dtype='interval[datetime64[ns], right]') @@ -1025,7 +1025,7 @@ def interval_range( endpoints of the individual intervals within the ``IntervalIndex``. For numeric ``start`` and ``end``, the frequency must also be numeric. - >>> pd.interval_range(start=0, periods=4, freq=1.5) + >>> pd.interval_range(start=0, periods=4, freq=1.5, inclusive="right") IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]], dtype='interval[float64, right]') @@ -1033,7 +1033,7 @@ def interval_range( convertible to a DateOffset. >>> pd.interval_range(start=pd.Timestamp('2017-01-01'), - ... periods=3, freq='MS') + ... periods=3, freq='MS', inclusive="right") IntervalIndex([(2017-01-01, 2017-02-01], (2017-02-01, 2017-03-01], (2017-03-01, 2017-04-01]], dtype='interval[datetime64[ns], right]') @@ -1041,7 +1041,7 @@ def interval_range( Specify ``start``, ``end``, and ``periods``; the frequency is generated automatically (linearly spaced). - >>> pd.interval_range(start=0, end=6, periods=4) + >>> pd.interval_range(start=0, end=6, periods=4, inclusive="right") IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]], dtype='interval[float64, right]') From d7dade54faaf040448c4d9fc4a605f53be163d05 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Sun, 27 Mar 2022 13:56:14 +0800 Subject: [PATCH 21/26] closed --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 3062de7fcf172..6431a756e335c 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1082,7 +1082,7 @@ of :ref:`frequency aliases ` with datetime-like inter pd.interval_range(start=pd.Timedelta("0 days"), periods=3, freq="9H") -Additionally, the ``closed`` parameter can be used to specify which side(s) the intervals +Additionally, the ``inclusive`` parameter can be used to specify which side(s) the intervals are closed on. Intervals are closed on the right side by default. .. ipython:: python From de0a29293cfc785576add571d54604ec6e582a45 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Sun, 27 Mar 2022 13:59:45 +0800 Subject: [PATCH 22/26] doc --- doc/source/user_guide/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/advanced.rst b/doc/source/user_guide/advanced.rst index 6431a756e335c..3081c6f7c6a08 100644 --- a/doc/source/user_guide/advanced.rst +++ b/doc/source/user_guide/advanced.rst @@ -1083,7 +1083,7 @@ of :ref:`frequency aliases ` with datetime-like inter pd.interval_range(start=pd.Timedelta("0 days"), periods=3, freq="9H") Additionally, the ``inclusive`` parameter can be used to specify which side(s) the intervals -are closed on. Intervals are closed on the right side by default. +are closed on. Intervals are closed on the both side by default. .. ipython:: python From 5154c093741acf8d36981ad30c1820c9edfb0ee0 Mon Sep 17 00:00:00 2001 From: Khor Chean Wei Date: Mon, 28 Mar 2022 00:07:35 +0800 Subject: [PATCH 23/26] Update interval.py --- pandas/core/indexes/interval.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 27dfd7c33a9c0..70bafaaac2b23 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1069,8 +1069,8 @@ def interval_range( inclusive = closed else: raise ValueError( - "Argument `closed` has to be either 'both', 'neither', 'left', 'right'," - "or 'both'" + "Argument `closed` has to be either" + "'both', 'neither', 'left' or 'right'" ) start = maybe_box_datetimelike(start) From ede24b67cb4cc8fa75cbd24b77041b6ec6c11016 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Mon, 28 Mar 2022 00:45:05 +0800 Subject: [PATCH 24/26] pre commit --- pandas/core/indexes/interval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 70bafaaac2b23..3d864658965d0 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1069,7 +1069,7 @@ def interval_range( inclusive = closed else: raise ValueError( - "Argument `closed` has to be either" + "Argument `closed` has to be either" "'both', 'neither', 'left' or 'right'" ) From dd16a4629c5362b07fe1641a2228910b6e3afcd1 Mon Sep 17 00:00:00 2001 From: "chean.wei.khor" Date: Tue, 29 Mar 2022 23:11:26 +0800 Subject: [PATCH 25/26] doc --- pandas/core/indexes/interval.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 3d864658965d0..9aabe4e9c9e1c 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -956,7 +956,7 @@ def interval_range( freq=None, name: Hashable = None, closed: lib.NoDefault = lib.no_default, - inclusive: str = "both", + inclusive: IntervalClosedType | None = None, ) -> IntervalIndex: """ Return a fixed frequency IntervalIndex. @@ -1072,6 +1072,8 @@ def interval_range( "Argument `closed` has to be either" "'both', 'neither', 'left' or 'right'" ) + elif inclusive is None: + inclusive = "both" start = maybe_box_datetimelike(start) end = maybe_box_datetimelike(end) From dc4658cfa557a6d3b1389fec7020f0779cc208c9 Mon Sep 17 00:00:00 2001 From: weikhor Date: Sun, 3 Apr 2022 15:22:11 +0800 Subject: [PATCH 26/26] test --- .../tests/indexes/interval/test_interval_range.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pandas/tests/indexes/interval/test_interval_range.py b/pandas/tests/indexes/interval/test_interval_range.py index 602fc35218520..63e7f3aa2b120 100644 --- a/pandas/tests/indexes/interval/test_interval_range.py +++ b/pandas/tests/indexes/interval/test_interval_range.py @@ -355,3 +355,17 @@ def test_errors(self): msg = "Start and end cannot both be tz-aware with different timezones" with pytest.raises(TypeError, match=msg): interval_range(start=start, end=end) + + def test_interval_range_error_and_warning(self): + # GH 40245 + + msg = ( + "Deprecated argument `closed` cannot " + "be passed if argument `inclusive` is not None" + ) + with pytest.raises(ValueError, match=msg): + interval_range(end=5, periods=4, closed="both", inclusive="both") + + msg = "Argument `closed` is deprecated in favor of `inclusive`" + with tm.assert_produces_warning(FutureWarning, match=msg): + interval_range(end=5, periods=4, closed="right")