Repository: pandas-dev/pandas
Pull request: REGR: Fix TypeError in groupby min / max of period column
- [x] closes #31471
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
TypeError when calculating min/max of period column using groupby
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd
periods = pd.period_range(start="2019-01", periods=4, freq="M")
groups = [1, 1, 2, 2]
df = pd.DataFrame({"periods": periods, "groups": groups})
result = df.groupby("groups")["periods"].min()
```
#### Problem description

The last line of the example throws a _TypeError: data type not understood_.

<details>
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    result = df.groupby("groups")["periods"].min()
  File "...\site-packages\pandas\core\groupby\groupby.py", line 1378, in f
    return self._cython_agg_general(alias, alt=npfunc, **kwargs)
  File "...\site-packages\pandas\core\groupby\groupby.py", line 889, in _cython_agg_general
    result, agg_names = self.grouper.aggregate(
  File "...\site-packages\pandas\core\groupby\ops.py", line 580, in aggregate
    return self._cython_operation(
  File "...\site-packages\pandas\core\groupby\ops.py", line 573, in _cython_operation
    result = result.astype(orig_values.dtype)
TypeError: data type not understood
</details>

#### Expected Output

The `result` DataFrame should contain the earliest period (in this case 2019-01) for each grouping slice.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS                                                        
------------------                                                        
commit           : None                                                   
python           : 3.8.1.final.0                                          
python-bits      : 64                                                     
OS               : Windows                                                
OS-release       : 10                                                     
machine          : AMD64                                                  
processor        : Intel64 Family 6 Model 142 Stepping 10, GenuineIntel   
byteorder        : little                                                 
LC_ALL           : None                                                   
LANG             : en_US.UTF-8                                            
LOCALE           : English_United Kingdom.1252                            
                                                                          
pandas           : 1.0.0                                                  
numpy            : 1.18.1                                                 
pytz             : 2019.3                                                 
dateutil         : 2.8.1                                                  
pip              : 20.0.2                                                 
setuptools       : 45.1.0.post20200127                                    
Cython           : None                                                   
pytest           : None                                                   
hypothesis       : None                                                   
sphinx           : None                                                   
blosc            : None                                                   
feather          : None                                                   
xlsxwriter       : None                                                   
lxml.etree       : None                                                   
html5lib         : None                                                   
pymysql          : None                                                   
psycopg2         : None                                                   
jinja2           : None                                                   
IPython          : None                                                   
pandas_datareader: None                                                   
bs4              : None                                                   
bottleneck       : None                                                   
fastparquet      : None                                                   
gcsfs            : None                                                   
lxml.etree       : None                                                   
matplotlib       : None                                                   
numexpr          : None                                                   
odfpy            : None                                                   
openpyxl         : None                                                   
pandas_gbq       : None                                                   
pyarrow          : None                                                   
pytables         : None                                                   
pytest           : None                                                   
pyxlsb           : None                                                   
s3fs             : None                                                   
scipy            : None                                                   
sqlalchemy       : None                                                   
tables           : None                                                   
tabulate         : None                                                   
xarray           : None                                                   
xlrd             : None                                                   
xlwt             : None                                                   
xlsxwriter       : None                                                   
numba            : None                                                   

</details>

Hints from issue discussion:

Commit message: Get rid of comma

Patch to review:
diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst
index 920919755dc23..e353cdfc10e80 100644
--- a/doc/source/whatsnew/v1.1.0.rst
+++ b/doc/source/whatsnew/v1.1.0.rst
@@ -179,6 +179,7 @@ Groupby/resample/rolling
 
 - Bug in :meth:`GroupBy.apply` raises ``ValueError`` when the ``by`` axis is not sorted and has duplicates and the applied ``func`` does not mutate passed in objects (:issue:`30667`)
 - Bug in :meth:`DataFrameGroupby.transform` produces incorrect result with transformation functions (:issue:`30918`)
+- Bug in :meth:`DataFrame.groupby` whereby taking the minimum or maximum of a column with period dtype would raise a ``TypeError``. (:issue:`31471`)
 
 Reshaping
 ^^^^^^^^^
diff --git a/pandas/core/groupby/ops.py b/pandas/core/groupby/ops.py
index 2e95daa392976..20774b921131c 100644
--- a/pandas/core/groupby/ops.py
+++ b/pandas/core/groupby/ops.py
@@ -31,6 +31,7 @@
     is_extension_array_dtype,
     is_integer_dtype,
     is_numeric_dtype,
+    is_period_dtype,
     is_sparse,
     is_timedelta64_dtype,
     needs_i8_conversion,
@@ -567,7 +568,9 @@ def _cython_operation(
         if swapped:
             result = result.swapaxes(0, axis)
 
-        if is_datetime64tz_dtype(orig_values.dtype):
+        if is_datetime64tz_dtype(orig_values.dtype) or is_period_dtype(
+            orig_values.dtype
+        ):
             result = type(orig_values)(result.astype(np.int64), dtype=orig_values.dtype)
         elif is_datetimelike and kind == "aggregate":
             result = result.astype(orig_values.dtype)
diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py
index eb9552fbbebc1..4cd0af40754ab 100644
--- a/pandas/tests/groupby/test_groupby.py
+++ b/pandas/tests/groupby/test_groupby.py
@@ -2037,3 +2037,19 @@ def test_groupby_list_level():
     expected = pd.DataFrame(np.arange(0, 9).reshape(3, 3))
     result = expected.groupby(level=[0]).mean()
     tm.assert_frame_equal(result, expected)
+
+
+@pytest.mark.parametrize("func", ["min", "max"])
+def test_groupby_aggregate_period(func):
+    # GH 31471
+    groups = [1, 2]
+    periods = pd.period_range("2020", periods=2, freq="Y")
+
+    df = pd.DataFrame({"a": groups, "b": periods})
+
+    result = getattr(df.groupby("a")["b"], func)()
+
+    idx = pd.Int64Index([1, 2], name="a")
+    expected = pd.Series(periods, index=idx, name="b")
+
+    tm.assert_series_equal(result, expected)


Code review comments:
diff_hunk: @@ -2037,3 +2037,19 @@ def test_groupby_list_level():
     expected = pd.DataFrame(np.arange(0, 9).reshape(3, 3))
     result = expected.groupby(level=[0]).mean()
     tm.assert_frame_equal(result, expected)
+
+
+@pytest.mark.parametrize("func", ["min", "max"])
path: pandas/tests/groupby/test_groupby.py
text: @user1:
should go to test_aggregate

diff_hunk: @@ -179,6 +179,7 @@ Groupby/resample/rolling
 
 - Bug in :meth:`GroupBy.apply` raises ``ValueError`` when the ``by`` axis is not sorted and has duplicates and the applied ``func`` does not mutate passed in objects (:issue:`30667`)
 - Bug in :meth:`DataFrameGroupby.transform` produces incorrect result with transformation functions (:issue:`30918`)
+- Bug in :meth:`DataFrame.groupby` whereby taking the minimum or maximum of a column with period dtype would raise a ``TypeError``. (:issue:`31471`)
path: doc/source/whatsnew/v1.1.0.rst
text: @user1:
Can you move this to the 1.01 whatsnew file? (this is a regression)