diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 8b4becebead29..15480cb2440ba 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -855,6 +855,7 @@ Groupby/resample/rolling - Bug in :meth:`pandas.core.window.Rolling.median` and :meth:`pandas.core.window.Rolling.quantile` where MemoryError is raised with empty window (:issue:`26005`) - Bug in :meth:`pandas.core.window.Rolling.median` and :meth:`pandas.core.window.Rolling.quantile` where incorrect results are returned with ``closed='left'`` and ``closed='neither'`` (:issue:`26005`) - Improved :class:`pandas.core.window.Rolling`, :class:`pandas.core.window.Window` and :class:`pandas.core.window.EWM` functions to exclude nuisance columns from results instead of raising errors and raise a ``DataError`` only if all columns are nuisance (:issue:`12537`) +- Bug in :meth:`pandas.core.window.Rolling.max` and :meth:`pandas.core.window.Rolling.min` where incorrect results are returned with an empty variable window`` (:issue:`26005`) Reshaping ^^^^^^^^^ diff --git a/pandas/_libs/window.pyx b/pandas/_libs/window.pyx index df86f395d6097..6203577e450d9 100644 --- a/pandas/_libs/window.pyx +++ b/pandas/_libs/window.pyx @@ -1313,9 +1313,11 @@ cdef _roll_min_max_variable(ndarray[numeric] values, # if right is open then the first window is empty close_offset = 0 if endi[0] > starti[0] else 1 + # first window's size + curr_win_size = endi[0] - starti[0] for i in range(endi[0], endi[N-1]): - if not Q.empty(): + if not Q.empty() and curr_win_size > 0: output[i-1+close_offset] = calc_mm( minp, nobs, values[Q.front()]) else: @@ -1344,7 +1346,7 @@ cdef _roll_min_max_variable(ndarray[numeric] values, Q.push_back(i) W.push_back(i) - if not Q.empty(): + if not Q.empty() and curr_win_size > 0: output[N-1] = calc_mm(minp, nobs, values[Q.front()]) else: output[N-1] = NaN diff --git a/pandas/tests/test_window.py b/pandas/tests/test_window.py index 889754841a078..8604acb1bd2b2 100644 --- a/pandas/tests/test_window.py +++ b/pandas/tests/test_window.py @@ -48,6 +48,12 @@ def win_types_special(request): return request.param +@pytest.fixture(params=["sum", "mean", "median", "max", "min", + "var", "std", "kurt", "skew"]) +def arithmetic_win_operators(request): + return request.param + + class Base: _nan_locs = np.arange(20, 40) @@ -522,6 +528,18 @@ def test_closed(self): with pytest.raises(ValueError): df.rolling(window=3, closed='neither') + @pytest.mark.parametrize("closed", ["neither", "left"]) + def test_closed_empty(self, closed, arithmetic_win_operators): + # GH 26005 + func_name = arithmetic_win_operators + ser = pd.Series(data=np.arange(5), + index=pd.date_range("2000", periods=5, freq="2D")) + roll = ser.rolling("1D", closed=closed) + + result = getattr(roll, func_name)() + expected = pd.Series([np.nan] * 5, index=ser.index) + tm.assert_series_equal(result, expected) + @pytest.mark.parametrize("func", ['min', 'max']) def test_closed_one_entry(self, func): # GH24718