Repository: hgrecco/pint-pandas

Issue:
`resample(...).std()` fails with NotImplementedError
Attempting to reduce the output of a resampler with `std()` fails with a NotImplementedError, as the case below shows (though, interestingly, it works for `mean()`). I see a (fixed) previous [issue](https://github.com/hgrecco/pint-pandas/issues/134) that looks related, but it seems this is taking a different code path through Pandas, so that fix doesn't seem to come into play.

Repro case:
```
import pandas as pd
import numpy as np
import pint_pandas
from pint import UnitRegistry

# Create a unit registry and grab kg units
ureg = UnitRegistry()
kg = ureg.kg

# Create a timeseries with 5-minute intervals over 6 hours, with random values between 0kg and 200kg
time_index = pd.date_range(
    start="2025-01-01 00:00",
    periods=int(6 * 60 / 5), 
    freq="5min"
)
values = np.random.uniform(low=0, high=200, size=len(time_index))
value_series = pd.Series(
    values,
    index=time_index,
    dtype=pint_pandas.PintType(kg)
)

# Attempt to resample and reduce
resampled = value_series.resample('h')
mean = resampled.mean()  # < --- Works
std = resampled.std()  # <--- Blows up with NotImplementedError
```

The full error is:
```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/resample.py", line 1432, in std
    return self._downsample("std", ddof=ddof, numeric_only=numeric_only)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/resample.py", line 1782, in _downsample
    result = obj.groupby(self._grouper).aggregate(how, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/generic.py", line 249, in aggregate
    return getattr(self, func)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/groupby.py", line 2647, in std
    return self._cython_agg_general(
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/groupby.py", line 2004, in _cython_agg_general
    new_mgr = data.grouped_reduce(array_func)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/internals/base.py", line 367, in grouped_reduce
    res = func(arr)
          ^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/groupby.py", line 1979, in array_func
    result = self._grouper._cython_operation(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/ops.py", line 831, in _cython_operation
    return cy_op.cython_operation(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/groupby/ops.py", line 541, in cython_operation
    return values._groupby_op(
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/abrown/.pyenv/versions/tc-3.11.10/lib/python3.11/site-packages/pandas/core/arrays/base.py", line 2398, in _groupby_op
    raise NotImplementedError(
NotImplementedError: function is not implemented for this dtype: pint[kilogram][Float64]
```

Discussion:

Fix (patch):
diff --git a/pint_pandas/pint_array.py b/pint_pandas/pint_array.py
index 3468c8c..6c09a50 100644
--- a/pint_pandas/pint_array.py
+++ b/pint_pandas/pint_array.py
@@ -1119,6 +1119,14 @@ class PintArray(ExtensionArray, ExtensionScalarOpsMixin):
                 result = functions[name](self.numpy_data, **kwds)
 
         return self._from_sequence(result, self.dtype)
+    
+    def _groupby_op(self, *args, **kwargs):
+        result = self._data._groupby_op(*args, **kwargs)
+        dtype = self.dtype
+        if kwargs['how'] == 'var':
+            dtype = PintType(units=f"pint[({self.units})**2]", subdtype=result.dtype)
+        return self._from_sequence(result, dtype)
+
 
 
 PintArray._add_arithmetic_ops()


Test changes:
diff --git a/pint_pandas/testsuite/test_issues.py b/pint_pandas/testsuite/test_issues.py
index 3d62951..5081aff 100644
--- a/pint_pandas/testsuite/test_issues.py
+++ b/pint_pandas/testsuite/test_issues.py
@@ -364,3 +364,43 @@ class TestIssue267(BaseExtensionTests):
 
         missing = pd.Series([False, True], name="mass")
         tm.assert_equal(pd.isna(mass), missing)
+
+
+class TestIssue285(BaseExtensionTests):
+    def test_issue285(self):
+        # Create a timeseries with 5-minute intervals over 6 hours, with random values between 0kg and 200kg
+        time_index = pd.date_range(
+            start="2025-01-01 00:00",
+            periods=5, 
+            freq="5min"
+        )
+
+    
+        values = np.random.uniform(low=0, high=200, size=len(time_index))
+        value_series = pd.Series(
+            values,
+            index=time_index,
+        )
+
+        # Attempt to resample and reduce
+        resampled = value_series.resample('h')
+        mean_expected = pd.Series(resampled.mean(), dtype=PintType("kg"))
+        std_expected = pd.Series(resampled.std(), dtype=PintType("kg"))
+        var_expected = pd.Series(resampled.var(), dtype=PintType("kg**2"))
+
+
+        value_series = pd.Series(
+            values,
+            index=time_index,
+            dtype=PintType("kg")
+        )
+
+        # Attempt to resample and reduce
+        resampled = value_series.resample('h')
+        mean_kg = resampled.mean()
+        std_kg = resampled.std() 
+        var_kg = resampled.var()
+
+        tm.assert_equal(mean_kg, mean_expected)
+        tm.assert_equal(std_kg, std_expected)
+        tm.assert_equal(var_kg, var_expected)