Skip to content

Commit dc48dc9

Browse files
ENH: Improve error message when rolling over NaT value (pandas-dev#46087)
1 parent 3ff438e commit dc48dc9

File tree

4 files changed

+28
-27
lines changed

4 files changed

+28
-27
lines changed

doc/source/whatsnew/v1.4.2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Bug fixes
3232
- Fix some cases for subclasses that define their ``_constructor`` properties as general callables (:issue:`46018`)
3333
- Fixed "longtable" formatting in :meth:`.Styler.to_latex` when ``column_format`` is given in extended format (:issue:`46037`)
3434
- Fixed incorrect rendering in :meth:`.Styler.format` with ``hyperlinks="html"`` when the url contains a colon or other special characters (:issue:`46389`)
35+
- Improved error message in :class:`~pandas.core.window.Rolling` when ``window`` is a frequency and ``NaT`` is in the rolling axis (:issue:`46087`)
3536

3637
.. ---------------------------------------------------------------------------
3738

pandas/core/window/rolling.py

+15-25
Original file line numberDiff line numberDiff line change
@@ -837,12 +837,6 @@ def _gotitem(self, key, ndim, subset=None):
837837
subset = self.obj.set_index(self._on)
838838
return super()._gotitem(key, ndim, subset=subset)
839839

840-
def _validate_monotonic(self):
841-
"""
842-
Validate that "on" is monotonic; already validated at a higher level.
843-
"""
844-
pass
845-
846840

847841
class Window(BaseWindow):
848842
"""
@@ -1687,7 +1681,7 @@ def _validate(self):
16871681
or isinstance(self._on, (DatetimeIndex, TimedeltaIndex, PeriodIndex))
16881682
) and isinstance(self.window, (str, BaseOffset, timedelta)):
16891683

1690-
self._validate_monotonic()
1684+
self._validate_datetimelike_monotonic()
16911685

16921686
# this will raise ValueError on non-fixed freqs
16931687
try:
@@ -1712,18 +1706,24 @@ def _validate(self):
17121706
elif not is_integer(self.window) or self.window < 0:
17131707
raise ValueError("window must be an integer 0 or greater")
17141708

1715-
def _validate_monotonic(self):
1709+
def _validate_datetimelike_monotonic(self):
17161710
"""
1717-
Validate monotonic (increasing or decreasing).
1711+
Validate self._on is monotonic (increasing or decreasing) and has
1712+
no NaT values for frequency windows.
17181713
"""
1714+
if self._on.hasnans:
1715+
self._raise_monotonic_error("values must not have NaT")
17191716
if not (self._on.is_monotonic_increasing or self._on.is_monotonic_decreasing):
1720-
self._raise_monotonic_error()
1717+
self._raise_monotonic_error("values must be monotonic")
17211718

1722-
def _raise_monotonic_error(self):
1723-
formatted = self.on
1724-
if self.on is None:
1725-
formatted = "index"
1726-
raise ValueError(f"{formatted} must be monotonic")
1719+
def _raise_monotonic_error(self, msg: str):
1720+
on = self.on
1721+
if on is None:
1722+
if self.axis == 0:
1723+
on = "index"
1724+
else:
1725+
on = "column"
1726+
raise ValueError(f"{on} {msg}")
17271727

17281728
@doc(
17291729
_shared_docs["aggregate"],
@@ -2630,13 +2630,3 @@ def _get_window_indexer(self) -> GroupbyIndexer:
26302630
indexer_kwargs=indexer_kwargs,
26312631
)
26322632
return window_indexer
2633-
2634-
def _validate_monotonic(self):
2635-
"""
2636-
Validate that on is monotonic;
2637-
"""
2638-
if (
2639-
not (self._on.is_monotonic_increasing or self._on.is_monotonic_decreasing)
2640-
or self._on.hasnans
2641-
):
2642-
self._raise_monotonic_error()

pandas/tests/window/test_groupby.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ def test_groupby_rolling_nans_in_index(self, rollings, key):
651651
)
652652
if key == "index":
653653
df = df.set_index("a")
654-
with pytest.raises(ValueError, match=f"{key} must be monotonic"):
654+
with pytest.raises(ValueError, match=f"{key} values must not have NaT"):
655655
df.groupby("c").rolling("60min", **rollings)
656656

657657
@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,
@@ -134,7 +135,7 @@ def test_non_monotonic_on(self):
134135

135136
assert not df.index.is_monotonic
136137

137-
msg = "index must be monotonic"
138+
msg = "index values must be monotonic"
138139
with pytest.raises(ValueError, match=msg):
139140
df.rolling("2s").sum()
140141

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

0 commit comments

Comments
 (0)