Repository: pandas-dev/pandas
Pull request: BUG: Series.resample fails on NaT index
- [x] closes #39227
- [x] tests added / passed
- [x] Ensure all linting tests pass, see [here](https://pandas.pydata.org/pandas-docs/dev/development/contributing.html#code-standards) for how to run them
- [x] whatsnew entry

This will also resolve the core issue of https://github.com/pandas-dev/pandas/pull/34998#discussion_r556463043. cc @jorisvandenbossche 

The added test:

```
    s = series.copy()
    s.index = PeriodIndex([NaT] * len(s), freq=freq)
    result = getattr(s.resample(freq), resample_method)()
```

currently fails for `freq="M"`, where upsample is used instead of downsample leading to failure since the index is not unique. I can't tell if this is a bug or an operation that should raise. It also fails for `DataFrame`, so I think is unrelated to the issue here.

cc @jbrockmendel 

Problem statement:
BUG: Series.resample fails on NaT PeriodIndex
Failure is only on Series and only when the index is entirely `NaT`.

```
freq = "D"

df = DataFrame([1, 2], index=PeriodIndex([NaT, NaT], freq=freq))
print(df.resample(freq).sum())

ser = Series([1, 2], index=PeriodIndex([NaT, NaT], freq=freq))
print(ser.resample(freq).sum())
```

Second raises, first returns the expected

```
Empty DataFrame
Columns: []
Index: []
```

Hints from issue discussion:

Commit message: Merge branch 'master' of https://github.com/pandas-dev/pandas into resampler_series_nat

Patch to review:
diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst
index 6a85bfd852e19..95784c780c4f5 100644
--- a/doc/source/whatsnew/v1.3.0.rst
+++ b/doc/source/whatsnew/v1.3.0.rst
@@ -339,6 +339,7 @@ Groupby/resample/rolling
 - Fixed bug in :meth:`DataFrameGroupBy.sum` and :meth:`SeriesGroupBy.sum` causing loss of precision through using Kahan summation (:issue:`38778`)
 - Fixed bug in :meth:`DataFrameGroupBy.cumsum`, :meth:`SeriesGroupBy.cumsum`, :meth:`DataFrameGroupBy.mean` and :meth:`SeriesGroupBy.mean` causing loss of precision through using Kahan summation (:issue:`38934`)
 - Bug in :meth:`.Resampler.aggregate` and :meth:`DataFrame.transform` raising ``TypeError`` instead of ``SpecificationError`` when missing keys had mixed dtypes (:issue:`39025`)
+- Bug in :meth:`Series.resample` would raise when index consisted of ``NaT`` (:issue:`39227`)
 
 Reshaping
 ^^^^^^^^^
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
index f6c1da723a1d9..75eb5a7687f85 100644
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -454,7 +454,8 @@ def _wrap_result(self, result):
 
         if isinstance(result, ABCSeries) and result.empty:
             obj = self.obj
-            result.index = _asfreq_compat(obj.index, freq=self.freq)
+            # When index is all NaT, result is empty but index is not
+            result.index = _asfreq_compat(obj.index[:0], freq=self.freq)
             result.name = getattr(obj, "name", None)
 
         return result
@@ -1653,10 +1654,17 @@ def _get_period_bins(self, ax: PeriodIndex):
             nat_count = np.sum(memb._isnan)
             memb = memb[~memb._isnan]
 
-        # if index contains no valid (non-NaT) values, return empty index
         if not len(memb):
-            binner = labels = PeriodIndex(data=[], freq=self.freq, name=ax.name)
-            return binner, [], labels
+            if len(ax) == 0:
+                # if index is empty, return empty bins
+                data = []
+                bins = np.array([], dtype=np.int64)
+            else:
+                # if index is all NaT, return a single bin
+                data = [NaT]
+                bins = np.array([len(ax)])
+            binner = labels = PeriodIndex(data=data, freq=self.freq, name=ax.name)
+            return binner, bins, labels
 
         freq_mult = self.freq.n
 
diff --git a/pandas/tests/resample/test_base.py b/pandas/tests/resample/test_base.py
index 7389fa31109f8..1154bc3316ae8 100644
--- a/pandas/tests/resample/test_base.py
+++ b/pandas/tests/resample/test_base.py
@@ -3,7 +3,7 @@
 import numpy as np
 import pytest
 
-from pandas import DataFrame, Series
+from pandas import DataFrame, NaT, PeriodIndex, Series
 import pandas._testing as tm
 from pandas.core.groupby.groupby import DataError
 from pandas.core.groupby.grouper import Grouper
@@ -110,6 +110,30 @@ def test_resample_empty_series(freq, empty_series_dti, resample_method):
     tm.assert_series_equal(result, expected, check_dtype=False)
 
 
+@all_ts
+@pytest.mark.parametrize("freq", ["M", "D", "H"])
+def test_resample_nat_index_series(request, freq, series, resample_method):
+    # GH39227
+
+    if freq == "M":
+        request.node.add_marker(pytest.mark.xfail(reason="Don't know why this fails"))
+
+    s = series.copy()
+    s.index = PeriodIndex([NaT] * len(s), freq=freq)
+    result = getattr(s.resample(freq), resample_method)()
+
+    if resample_method == "ohlc":
+        expected = DataFrame(
+            [], index=s.index[:0].copy(), columns=["open", "high", "low", "close"]
+        )
+        tm.assert_frame_equal(result, expected, check_dtype=False)
+    else:
+        expected = s[:0].copy()
+        tm.assert_series_equal(result, expected, check_dtype=False)
+    tm.assert_index_equal(result.index, expected.index)
+    assert result.index.freq == expected.index.freq
+
+
 @all_ts
 @pytest.mark.parametrize("freq", ["M", "D", "H"])
 @pytest.mark.parametrize("resample_method", ["count", "size"])
diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py
index e83196e9c7d56..2fe3fb91768e6 100644
--- a/pandas/tests/resample/test_period_index.py
+++ b/pandas/tests/resample/test_period_index.py
@@ -787,9 +787,9 @@ def test_resample_with_nat(self, periods, values, freq, expected_values):
     def test_resample_with_only_nat(self):
         # GH 13224
         pi = PeriodIndex([pd.NaT] * 3, freq="S")
-        frame = DataFrame([2, 3, 5], index=pi)
+        frame = DataFrame([2, 3, 5], index=pi, columns=["a"])
         expected_index = PeriodIndex(data=[], freq=pi.freq)
-        expected = DataFrame(index=expected_index)
+        expected = DataFrame(index=expected_index, columns=["a"], dtype="int64")
         result = frame.resample("1s").mean()
         tm.assert_frame_equal(result, expected)
 


Code review comments:
diff_hunk: @@ -339,6 +339,7 @@ Groupby/resample/rolling
 - Fixed bug in :meth:`DataFrameGroupBy.sum` and :meth:`SeriesGroupBy.sum` causing loss of precision through using Kahan summation (:issue:`38778`)
 - Fixed bug in :meth:`DataFrameGroupBy.cumsum`, :meth:`SeriesGroupBy.cumsum`, :meth:`DataFrameGroupBy.mean` and :meth:`SeriesGroupBy.mean` causing loss of precision through using Kahan summation (:issue:`38934`)
 - Bug in :meth:`.Resampler.aggregate` and :meth:`DataFrame.transform` raising ``TypeError`` instead of ``SpecificationError`` when missing keys had mixed dtypes (:issue:`39025`)
+- Bug in :meth:`Series.resample` would raise when index consisted of ``NaT`` (:issue:`39227`)
path: doc/source/whatsnew/v1.3.0.rst
text: @user1:
Probably mention that this is specific to `PeriodIndex`?

Construction with a `DatetimeIndex` in your original example raises before `resampling` so the bug fix shouldnt apply to it

```
In [9]: ser = Series([1, 2], index=DatetimeIndex([NaT, NaT], freq=freq))

ValueError: Neither `start` nor `end` can be NaT

The above exception was the direct cause of the following exception:

ValueError: Inferred frequency None from passed values does not conform to passed frequency D
```

@author:
Thanks!