Skip to content

Commit

Permalink
Backport PR #40628: ENH: New boundary inputs (#42217)
Browse files Browse the repository at this point in the history
Co-authored-by: Kiley Hewitt <42876297+hewittk@users.noreply.github.com>
  • Loading branch information
meeseeksmachine and hewittk committed Jun 25, 2021
1 parent 2441acd commit 0da17ce
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v1.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ Other enhancements
- Add keyword ``dropna`` to :meth:`DataFrame.value_counts` to allow counting rows that include ``NA`` values (:issue:`41325`)
- :meth:`Series.replace` will now cast results to ``PeriodDtype`` where possible instead of ``object`` dtype (:issue:`41526`)
- Improved error message in ``corr`` and ``cov`` methods on :class:`.Rolling`, :class:`.Expanding`, and :class:`.ExponentialMovingWindow` when ``other`` is not a :class:`DataFrame` or :class:`Series` (:issue:`41741`)
- :meth:`Series.between` can now accept ``left`` or ``right`` as arguments to ``inclusive`` to include only the left or right boundary (:issue:`40245`)
- :meth:`DataFrame.explode` now supports exploding multiple columns. Its ``column`` argument now also accepts a list of str or tuples for exploding on multiple columns at the same time (:issue:`39240`)

.. ---------------------------------------------------------------------------
Expand Down Expand Up @@ -838,6 +839,7 @@ Other Deprecations
- Deprecated inference of ``timedelta64[ns]``, ``datetime64[ns]``, or ``DatetimeTZDtype`` dtypes in :class:`Series` construction when data containing strings is passed and no ``dtype`` is passed (:issue:`33558`)
- In a future version, constructing :class:`Series` or :class:`DataFrame` with ``datetime64[ns]`` data and ``DatetimeTZDtype`` will treat the data as wall-times instead of as UTC times (matching DatetimeIndex behavior). To treat the data as UTC times, use ``pd.Series(data).dt.tz_localize("UTC").dt.tz_convert(dtype.tz)`` or ``pd.Series(data.view("int64"), dtype=dtype)`` (:issue:`33401`)
- Deprecated passing lists as ``key`` to :meth:`DataFrame.xs` and :meth:`Series.xs` (:issue:`41760`)
- Deprecated boolean arguments of ``inclusive`` in :meth:`Series.between` to have ``{"left", "right", "neither", "both"}`` as standard argument values (:issue:`40628`)
- Deprecated passing arguments as positional for all of the following, with exceptions noted (:issue:`41485`):

- :func:`concat` (other than ``objs``)
Expand Down
33 changes: 28 additions & 5 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4967,7 +4967,7 @@ def isin(self, values) -> Series:
self, method="isin"
)

def between(self, left, right, inclusive=True) -> Series:
def between(self, left, right, inclusive="both") -> Series:
"""
Return boolean Series equivalent to left <= series <= right.
Expand All @@ -4981,8 +4981,9 @@ def between(self, left, right, inclusive=True) -> Series:
Left boundary.
right : scalar or list-like
Right boundary.
inclusive : bool, default True
Include boundaries.
inclusive : {"both", "neither", "left", "right"}
Include boundaries. Whether to set each bound as closed or open.
.. versionchanged:: 1.3.0
Returns
-------
Expand Down Expand Up @@ -5033,12 +5034,34 @@ def between(self, left, right, inclusive=True) -> Series:
3 False
dtype: bool
"""
if inclusive:
if inclusive is True or inclusive is False:
warnings.warn(
"Boolean inputs to the `inclusive` argument are deprecated in"
"favour of `both` or `neither`.",
FutureWarning,
stacklevel=2,
)
if inclusive:
inclusive = "both"
else:
inclusive = "neither"
if inclusive == "both":
lmask = self >= left
rmask = self <= right
else:
elif inclusive == "left":
lmask = self >= left
rmask = self < right
elif inclusive == "right":
lmask = self > left
rmask = self <= right
elif inclusive == "neither":
lmask = self > left
rmask = self < right
else:
raise ValueError(
"Inclusive has to be either string of 'both',"
"'left', 'right', or 'neither'."
)

return lmask & rmask

Expand Down
48 changes: 47 additions & 1 deletion pandas/tests/series/methods/test_between.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import pytest

from pandas import (
Series,
Expand Down Expand Up @@ -28,7 +29,7 @@ def test_between_datetime_values(self):
expected = ser[3:18].dropna()
tm.assert_series_equal(result, expected)

result = ser[ser.between(ser[3], ser[17], inclusive=False)]
result = ser[ser.between(ser[3], ser[17], inclusive="neither")]
expected = ser[5:16].dropna()
tm.assert_series_equal(result, expected)

Expand All @@ -38,3 +39,48 @@ def test_between_period_values(self):
result = ser.between(left, right)
expected = (ser >= left) & (ser <= right)
tm.assert_series_equal(result, expected)

def test_between_inclusive_string(self): # :issue:`40628`
series = Series(date_range("1/1/2000", periods=10))
left, right = series[[2, 7]]

result = series.between(left, right, inclusive="both")
expected = (series >= left) & (series <= right)
tm.assert_series_equal(result, expected)

result = series.between(left, right, inclusive="left")
expected = (series >= left) & (series < right)
tm.assert_series_equal(result, expected)

result = series.between(left, right, inclusive="right")
expected = (series > left) & (series <= right)
tm.assert_series_equal(result, expected)

result = series.between(left, right, inclusive="neither")
expected = (series > left) & (series < right)
tm.assert_series_equal(result, expected)

def test_between_error_args(self): # :issue:`40628`
series = Series(date_range("1/1/2000", periods=10))
left, right = series[[2, 7]]

value_error_msg = (
"Inclusive has to be either string of 'both',"
"'left', 'right', or 'neither'."
)

with pytest.raises(ValueError, match=value_error_msg):
series = Series(date_range("1/1/2000", periods=10))
series.between(left, right, inclusive="yes")

def test_between_inclusive_warning(self):
series = Series(date_range("1/1/2000", periods=10))
left, right = series[[2, 7]]
with tm.assert_produces_warning(FutureWarning):
result = series.between(left, right, inclusive=False)
expected = (series > left) & (series < right)
tm.assert_series_equal(result, expected)
with tm.assert_produces_warning(FutureWarning):
result = series.between(left, right, inclusive=True)
expected = (series >= left) & (series <= right)
tm.assert_series_equal(result, expected)

0 comments on commit 0da17ce

Please sign in to comment.