diff --git a/doc/source/whatsnew/v1.3.2.rst b/doc/source/whatsnew/v1.3.2.rst index bcb096e630d85..70fff75f84dc3 100644 --- a/doc/source/whatsnew/v1.3.2.rst +++ b/doc/source/whatsnew/v1.3.2.rst @@ -32,6 +32,7 @@ Bug fixes ~~~~~~~~~ - Bug in :meth:`pandas.read_excel` modifies the dtypes dictionary when reading a file with duplicate columns (:issue:`42462`) - 1D slices over extension types turn into N-dimensional slices over ExtensionArrays (:issue:`42430`) +- Fixed bug in :meth:`Series.rolling` and :meth:`DataFrame.rolling` not calculating window bounds correctly for the first row when ``center=True`` and ``window`` is an offset that covers all the rows (:issue:`42753`) - :meth:`.Styler.hide_columns` now hides the index name header row as well as column headers (:issue:`42101`) - Bug in de-serializing datetime indexes in PYTHONOPTIMIZED mode (:issue:`42866`) - diff --git a/pandas/_libs/window/indexers.pyx b/pandas/_libs/window/indexers.pyx index d188770576e05..197345b3ce6ac 100644 --- a/pandas/_libs/window/indexers.pyx +++ b/pandas/_libs/window/indexers.pyx @@ -79,12 +79,11 @@ def calculate_variable_window_bounds( else: end[0] = 0 if center: - for j in range(0, num_values + 1): - if (index[j] == index[0] + index_growth_sign * window_size / 2 and - right_closed): + end_bound = index[0] + index_growth_sign * window_size / 2 + for j in range(0, num_values): + if (index[j] < end_bound) or (index[j] == end_bound and right_closed): end[0] = j + 1 - break - elif index[j] >= index[0] + index_growth_sign * window_size / 2: + elif index[j] >= end_bound: end[0] = j break diff --git a/pandas/tests/window/test_rolling.py b/pandas/tests/window/test_rolling.py index 77ca482936298..5bf2df0208ddc 100644 --- a/pandas/tests/window/test_rolling.py +++ b/pandas/tests/window/test_rolling.py @@ -220,6 +220,36 @@ def test_datetimelike_centered_selections( tm.assert_frame_equal(result, expected, check_dtype=False) +@pytest.mark.parametrize( + "window,closed,expected", + [ + ("3s", "right", [3.0, 3.0, 3.0]), + ("3s", "both", [3.0, 3.0, 3.0]), + ("3s", "left", [3.0, 3.0, 3.0]), + ("3s", "neither", [3.0, 3.0, 3.0]), + ("2s", "right", [3.0, 2.0, 2.0]), + ("2s", "both", [3.0, 3.0, 3.0]), + ("2s", "left", [1.0, 3.0, 3.0]), + ("2s", "neither", [1.0, 2.0, 2.0]), + ], +) +def test_datetimelike_centered_offset_covers_all( + window, closed, expected, frame_or_series +): + # GH 42753 + + index = [ + Timestamp("20130101 09:00:01"), + Timestamp("20130101 09:00:02"), + Timestamp("20130101 09:00:02"), + ] + df = frame_or_series([1, 1, 1], index=index) + + result = df.rolling(window, closed=closed, center=True).sum() + expected = frame_or_series(expected, index=index) + tm.assert_equal(result, expected) + + def test_even_number_window_alignment(): # see discussion in GH 38780 s = Series(range(3), index=date_range(start="2020-01-01", freq="D", periods=3))