Repository: pandas-dev/pandas
Pull request: [BUG]: Rolling selected too large windows with PeriodIndex
- [x] closes #34225
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

``asi8`` of PeriodIndex is not in nanoseconds, so we can not set ``self.window=freq.nanos``

Problem statement:
BUG: Use of PeriodIndex with rolling transform aggregating function into cumulative aggregating?
- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the latest version of pandas.

---
#### Code Sample, a copy-pastable example

```python
import pandas as pd
start = '2020-01-01 08:00'
end = '2020-01-01 12:00'
intervals = pd.period_range(start=start, end=end, freq = '30T')
values = [i for i in range(0, len(intervals))]
ser = pd.Series(values, index = intervals)
offset = pd.tseries.frequencies.to_offset('1h')

test_min = ser.rolling(window = offset, closed='left').min()
test_sum = ser.rolling(window = offset, closed='left').sum()
test_max = ser.rolling(window = offset, closed='left').max()
```
#### Output
```python
# Problem 1
test_min
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    0.0
2020-01-01 09:00    0.0
2020-01-01 09:30    0.0
2020-01-01 10:00    0.0
2020-01-01 10:30    0.0
2020-01-01 11:00    0.0
2020-01-01 11:30    0.0
2020-01-01 12:00    0.0
Freq: 30T, dtype: float64
```
```python
# Problem 1
test_sum
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    0.0
2020-01-01 09:00    1.0
2020-01-01 09:30    3.0
2020-01-01 10:00    6.0
2020-01-01 10:30   10.0
2020-01-01 11:00   15.0
2020-01-01 11:30   21.0
2020-01-01 12:00   28.0
Freq: 30T, dtype: float64
```
```python
# Problem 2
test_max
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    0.0
2020-01-01 09:00    1.0
2020-01-01 09:30    2.0
2020-01-01 10:00    3.0
2020-01-01 10:30    4.0
2020-01-01 11:00    5.0
2020-01-01 11:30    6.0
2020-01-01 12:00    7.0
Freq: 30T, dtype: float64
```
#### Problem descriptions
There are 2 problems:
  * problem 1:
     * min value on a `1H` window is not 0 given the input provided (see expected output)
     * 2nd example with sum helps understand what happens with min: rolling is actually operating cumulated sum and cumulated min?!
  * problem 2: the 0 at the second row in `test_max` shows that rolling with closed = 'left' is not able to handle PeriodIndex correctly. It seems it does not know that periods are themselves bins with a specific close parameter to be considered.

#### Expected Output
```python
# Problem 1
test_min
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    0.0
2020-01-01 09:00    1.0
2020-01-01 09:30    2.0
2020-01-01 10:00    3.0
2020-01-01 10:30    4.0
2020-01-01 11:00    5.0
2020-01-01 11:30    6.0
2020-01-01 12:00    7.0
Freq: 30T, dtype: float64
```
```python
# Problem 1
test_sum
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    1.0
2020-01-01 09:00    3.0
2020-01-01 09:30    5.0
2020-01-01 10:00    7.0
2020-01-01 10:30    9.0
2020-01-01 11:00   11.0
2020-01-01 11:30   13.0
2020-01-01 12:00   15.0
Freq: 30T, dtype: float64
```
```python
# Problem 2
test_max
```
```python
2020-01-01 08:00    NaN
2020-01-01 08:30    1.0
2020-01-01 09:00    2.0
2020-01-01 09:30    3.0
2020-01-01 10:00    4.0
2020-01-01 10:30    5.0
2020-01-01 11:00    6.0
2020-01-01 11:30    7.0
2020-01-01 12:00    8.0
Freq: 30T, dtype: float64
```
```
#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit           : None
python           : 3.7.6.final.0
python-bits      : 64
OS               : Linux
OS-release       : 5.3.0-51-generic
machine          : x86_64
processor        : x86_64
byteorder        : little
LC_ALL           : None
LANG             : fr_FR.UTF-8
LOCALE           : fr_FR.UTF-8
pandas           : 1.0.3
numpy            : 1.16.3
pytz             : 2020.1
dateutil         : 2.8.1
pip              : 20.0.2
setuptools       : 46.2.0.post20200511
Cython           : None
pytest           : None
hypothesis       : None
sphinx           : 3.0.3
blosc            : None
feather          : None
xlsxwriter       : None
lxml.etree       : None
html5lib         : None
pymysql          : None
psycopg2         : None
jinja2           : 2.11.2
IPython          : 7.13.0
pandas_datareader: None
bs4              : None
bottleneck       : None
fastparquet      : 0.3.3
gcsfs            : None
lxml.etree       : None
matplotlib       : 3.0.3
numexpr          : 2.7.1
odfpy            : None
openpyxl         : None
pandas_gbq       : None
pyarrow          : 0.16.0
pytables         : None
pytest           : None
pyxlsb           : None
s3fs             : None
scipy            : None
sqlalchemy       : None
tables           : 3.6.1
tabulate         : None
xarray           : None
xlrd             : None
xlwt             : None
xlsxwriter       : None
numba            : 0.48.0

</details>

Hints from issue discussion:

Commit message: Fix bug with differing units

Patch to review:
diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst
index 7ba64f57be136..02969a05e05f5 100644
--- a/doc/source/whatsnew/v1.2.0.rst
+++ b/doc/source/whatsnew/v1.2.0.rst
@@ -347,6 +347,7 @@ Groupby/resample/rolling
 - Bug in :meth:`DataFrame.groupby` does not always maintain column index name for ``any``, ``all``, ``bfill``, ``ffill``, ``shift`` (:issue:`29764`)
 - Bug in :meth:`DataFrameGroupBy.apply` raising error with ``np.nan`` group(s) when ``dropna=False`` (:issue:`35889`)
 - Bug in :meth:`Rolling.sum()` returned wrong values when dtypes where mixed between float and integer and axis was equal to one (:issue:`20649`, :issue:`35596`)
+- Bug in :meth:`Rolling` when using ``PeriodIndex`` selected too large windows when applying a function onto the ``Rolling`` object (:issue:`34225`)
 
 Reshaping
 ^^^^^^^^^
diff --git a/pandas/core/window/rolling.py b/pandas/core/window/rolling.py
index 335fc3db5cd86..fbfdd7909faf1 100644
--- a/pandas/core/window/rolling.py
+++ b/pandas/core/window/rolling.py
@@ -1959,7 +1959,10 @@ def validate(self):
 
             # this will raise ValueError on non-fixed freqs
             self.win_freq = self.window
-            self.window = freq.nanos
+            if isinstance(self._on, ABCPeriodIndex):
+                self.window = freq.nanos / (self._on.freq.nanos / self._on.freq.n)
+            else:
+                self.window = freq.nanos
             self.win_type = "freq"
 
             # min_periods must be an integer
diff --git a/pandas/tests/window/test_rolling.py b/pandas/tests/window/test_rolling.py
index 4dfa0287bbb03..226c7eb722eb1 100644
--- a/pandas/tests/window/test_rolling.py
+++ b/pandas/tests/window/test_rolling.py
@@ -819,3 +819,34 @@ def test_rolling_axis_1_non_numeric_dtypes(value):
     result = df.rolling(window=2, min_periods=1, axis=1).sum()
     expected = pd.DataFrame({"a": [1.0, 2.0]})
     tm.assert_frame_equal(result, expected)
+
+
+@pytest.mark.parametrize(
+    ("index", "window"),
+    [
+        (
+            pd.period_range(start="2020-01-01 08:00", end="2020-01-01 08:08", freq="T"),
+            "2T",
+        ),
+        (
+            pd.period_range(
+                start="2020-01-01 08:00", end="2020-01-01 12:00", freq="30T"
+            ),
+            "1h",
+        ),
+    ],
+)
+@pytest.mark.parametrize(
+    ("func", "values"),
+    [
+        ("min", [np.nan, 0, 0, 1, 2, 3, 4, 5, 6]),
+        ("max", [np.nan, 0, 1, 2, 3, 4, 5, 6, 7]),
+        ("sum", [np.nan, 0, 1, 3, 5, 7, 9, 11, 13]),
+    ],
+)
+def test_rolling_period_index(index, window, func, values):
+    # GH: 34225
+    ds = pd.Series([0, 1, 2, 3, 4, 5, 6, 7, 8], index=index)
+    result = getattr(ds.rolling(window, closed="left"), func)()
+    expected = pd.Series(values, index=index)
+    tm.assert_series_equal(result, expected)


Code review comments:
diff_hunk: @@ -1959,7 +1959,10 @@ def validate(self):
 
             # this will raise ValueError on non-fixed freqs
             self.win_freq = self.window
-            self.window = freq.nanos
+            if isinstance(self._on, ABCPeriodIndex):
path: pandas/core/window/rolling.py
text: @user1:
Can you create a helper method to do this instead?

@author:
Thx, something like this?