Skip to content

Commit 77d9237

Browse files
authored
ENH: Improve error message when rolling over NaT value (#46087)
1 parent d391721 commit 77d9237

File tree

4 files changed

+28
-27
lines changed

4 files changed

+28
-27
lines changed

doc/source/whatsnew/v1.5.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Other enhancements
4040
- :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba <https://numba.pydata.org/>`_ execution with the ``engine`` keyword (:issue:`45428`)
4141
- Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype array-like to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`)
4242
- Implemented a complex-dtype :class:`Index`, passing a complex-dtype array-like to ``pd.Index`` will now retain complex dtype instead of casting to ``object`` (:issue:`45845`)
43+
- Improved error message in :class:`~pandas.core.window.Rolling` when ``window`` is a frequency and ``NaT`` is in the rolling axis (:issue:`46087`)
4344
- :class:`Series` and :class:`DataFrame` with ``IntegerDtype`` now supports bitwise operations (:issue:`34463`)
4445
-
4546

pandas/core/window/rolling.py

+15-25
Original file line numberDiff line numberDiff line change
@@ -827,12 +827,6 @@ def _gotitem(self, key, ndim, subset=None):
827827
subset = self.obj.set_index(self._on)
828828
return super()._gotitem(key, ndim, subset=subset)
829829

830-
def _validate_monotonic(self):
831-
"""
832-
Validate that "on" is monotonic; already validated at a higher level.
833-
"""
834-
pass
835-
836830

837831
class Window(BaseWindow):
838832
"""
@@ -1661,7 +1655,7 @@ def _validate(self):
16611655
or isinstance(self._on, (DatetimeIndex, TimedeltaIndex, PeriodIndex))
16621656
) and isinstance(self.window, (str, BaseOffset, timedelta)):
16631657

1664-
self._validate_monotonic()
1658+
self._validate_datetimelike_monotonic()
16651659

16661660
# this will raise ValueError on non-fixed freqs
16671661
try:
@@ -1692,18 +1686,24 @@ def _validate(self):
16921686
elif not is_integer(self.window) or self.window < 0:
16931687
raise ValueError("window must be an integer 0 or greater")
16941688

1695-
def _validate_monotonic(self):
1689+
def _validate_datetimelike_monotonic(self):
16961690
"""
1697-
Validate monotonic (increasing or decreasing).
1691+
Validate self._on is monotonic (increasing or decreasing) and has
1692+
no NaT values for frequency windows.
16981693
"""
1694+
if self._on.hasnans:
1695+
self._raise_monotonic_error("values must not have NaT")
16991696
if not (self._on.is_monotonic_increasing or self._on.is_monotonic_decreasing):
1700-
self._raise_monotonic_error()
1697+
self._raise_monotonic_error("values must be monotonic")
17011698

1702-
def _raise_monotonic_error(self):
1703-
formatted = self.on
1704-
if self.on is None:
1705-
formatted = "index"
1706-
raise ValueError(f"{formatted} must be monotonic")
1699+
def _raise_monotonic_error(self, msg: str):
1700+
on = self.on
1701+
if on is None:
1702+
if self.axis == 0:
1703+
on = "index"
1704+
else:
1705+
on = "column"
1706+
raise ValueError(f"{on} {msg}")
17071707

17081708
@doc(
17091709
_shared_docs["aggregate"],
@@ -2610,13 +2610,3 @@ def _get_window_indexer(self) -> GroupbyIndexer:
26102610
indexer_kwargs=indexer_kwargs,
26112611
)
26122612
return window_indexer
2613-
2614-
def _validate_monotonic(self):
2615-
"""
2616-
Validate that on is monotonic;
2617-
"""
2618-
if (
2619-
not (self._on.is_monotonic_increasing or self._on.is_monotonic_decreasing)
2620-
or self._on.hasnans
2621-
):
2622-
self._raise_monotonic_error()

pandas/tests/window/test_groupby.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ def test_groupby_rolling_nans_in_index(self, rollings, key):
678678
)
679679
if key == "index":
680680
df = df.set_index("a")
681-
with pytest.raises(ValueError, match=f"{key} must be monotonic"):
681+
with pytest.raises(ValueError, match=f"{key} values must not have NaT"):
682682
df.groupby("c").rolling("60min", **rollings)
683683

684684
@pytest.mark.parametrize("group_keys", [True, False])

pandas/tests/window/test_timeseries_window.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
DataFrame,
66
Index,
77
MultiIndex,
8+
NaT,
89
Series,
910
Timestamp,
1011
date_range,
@@ -139,7 +140,7 @@ def test_non_monotonic_on(self):
139140

140141
assert not df.index.is_monotonic_increasing
141142

142-
msg = "index must be monotonic"
143+
msg = "index values must be monotonic"
143144
with pytest.raises(ValueError, match=msg):
144145
df.rolling("2s").sum()
145146

@@ -762,3 +763,12 @@ def test_rolling_on_multi_index_level(self):
762763
{"column": [0.0, 1.0, 3.0, 6.0, 10.0, 15.0]}, index=df.index
763764
)
764765
tm.assert_frame_equal(result, expected)
766+
767+
768+
@pytest.mark.parametrize("msg, axis", [["column", 1], ["index", 0]])
769+
def test_nat_axis_error(msg, axis):
770+
idx = [Timestamp("2020"), NaT]
771+
kwargs = {"columns" if axis == 1 else "index": idx}
772+
df = DataFrame(np.eye(2), **kwargs)
773+
with pytest.raises(ValueError, match=f"{msg} values must not have NaT"):
774+
df.rolling("D", axis=axis).mean()

0 commit comments

Comments
 (0)