Repository: pydata/xarray

Issue:
Default behaviour of `min_count` wrong with flox
### What happened?

```python
with xr.set_options(display_style="text", use_flox=False):
    with xr.set_options(use_flox=False):
        display(
            xr.DataArray(
                data=np.array([np.nan, 1, 1, np.nan, 1, 1]),
                dims="x",
                coords={"labels": ("x", np.array([1, 2, 3, 1, 2, 3]))},
            )
            .groupby("labels")
            .sum()
        )

    with xr.set_options(use_flox=True):
        display(
            xr.DataArray(
                data=np.array([np.nan, 1, 1, np.nan, 1, 1]),
                dims="x",
                coords={"labels": ("x", np.array([1, 2, 3, 1, 2, 3]))},
            )
            .groupby("labels")
            .sum()
        )
```

```
# without flox
<xarray.DataArray (labels: 3)>
array([0., 2., 2.])
Coordinates:
  * labels   (labels) int64 1 2 3

# with flox
<xarray.DataArray (labels: 3)>
array([nan,  2.,  2.])
Coordinates:
  * labels   (labels) int64 1 2 3
```

### What did you expect to happen?

The same answer. We should set `min_count=0` when `min_count is None`

### Minimal Complete Verifiable Example

_No response_

### MVCE confirmation

- [ ] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- [ ] Complete example — the example is self-contained, including all data and the text of any traceback.
- [ ] Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result.
- [ ] New issue — a search of GitHub Issues suggests this is not a duplicate.

### Relevant log output

_No response_

### Anything else we need to know?

_No response_

### Environment

<details>



</details>


Discussion:

Patch:
diff --git a/.github/workflows/pypi-release.yaml b/.github/workflows/pypi-release.yaml
index 41957a94..cdfdba4e 100644
--- a/.github/workflows/pypi-release.yaml
+++ b/.github/workflows/pypi-release.yaml
@@ -72,7 +72,7 @@ jobs:
 
       - name: Publish package to TestPyPI
         if: github.event_name == 'push'
-        uses: pypa/gh-action-pypi-publish@v1.8.5
+        uses: pypa/gh-action-pypi-publish@v1.8.6
         with:
           user: __token__
           password: ${{ secrets.TESTPYPI_TOKEN }}
@@ -90,7 +90,7 @@ jobs:
           name: releases
           path: dist
       - name: Publish package to PyPI
-        uses: pypa/gh-action-pypi-publish@v1.8.5
+        uses: pypa/gh-action-pypi-publish@v1.8.6
         with:
           user: __token__
           password: ${{ secrets.PYPI_TOKEN }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9d111004..4ddd39ff 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,7 +16,7 @@ repos:
         files: ^xarray/
   - repo: https://github.com/charliermarsh/ruff-pre-commit
     # Ruff version.
-    rev: 'v0.0.263'
+    rev: 'v0.0.265'
     hooks:
       - id: ruff
         args: ["--fix"]
diff --git a/doc/contributing.rst b/doc/contributing.rst
index b8195d12..3cc43314 100644
--- a/doc/contributing.rst
+++ b/doc/contributing.rst
@@ -1069,7 +1069,7 @@ PR checklist
 
   - Write new tests if needed. See `"Test-driven development/code writing" <https://docs.xarray.dev/en/stable/contributing.html#test-driven-development-code-writing>`_.
   - Test the code using `Pytest <http://doc.pytest.org/en/latest/>`_. Running all tests (type ``pytest`` in the root directory) takes a while, so feel free to only run the tests you think are needed based on your PR (example: ``pytest xarray/tests/test_dataarray.py``). CI will catch any failing tests.
-  - By default, the upstream dev CI is disabled on pull request and push events. You can override this behavior per commit by adding a <tt>[test-upstream]</tt> tag to the first line of the commit message. For documentation-only commits, you can skip the CI per commit by adding a "[skip-ci]" tag to the first line of the commit message.
+  - By default, the upstream dev CI is disabled on pull request and push events. You can override this behavior per commit by adding a ``[test-upstream]`` tag to the first line of the commit message. For documentation-only commits, you can skip the CI per commit by adding a ``[skip-ci]`` tag to the first line of the commit message.
 
 - **Properly format your code** and verify that it passes the formatting guidelines set by `Black <https://black.readthedocs.io/en/stable/>`_ and `Flake8 <http://flake8.pycqa.org/en/latest/>`_. See `"Code formatting" <https://docs.xarray.dev/en/stablcontributing.html#code-formatting>`_. You can use `pre-commit <https://pre-commit.com/>`_ to run these automatically on each commit.
 
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index b94a8a2f..45273d31 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -42,6 +42,8 @@ Bug fixes
 ~~~~~~~~~
 - Fix groupby binary ops when grouped array is subset relative to other. (:issue:`7797`).
   By `Deepak Cherian <https://github.com/dcherian>`_.
+- Fix groupby sum, prod for all-NaN groups with ``flox``. (:issue:`7808`).
+  By `Deepak Cherian <https://github.com/dcherian>`_.
 
 Documentation
 ~~~~~~~~~~~~~
diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py
index 55fe103d..85ea0b5f 100644
--- a/xarray/core/groupby.py
+++ b/xarray/core/groupby.py
@@ -964,6 +964,13 @@ class GroupBy(Generic[T_Xarray]):
         else:
             non_numeric = {}
 
+        if "min_count" in kwargs:
+            if kwargs["func"] not in ["sum", "prod"]:
+                raise TypeError("Received an unexpected keyword argument 'min_count'")
+            elif kwargs["min_count"] is None:
+                # set explicitly to avoid unncessarily accumulating count
+                kwargs["min_count"] = 0
+
         # weird backcompat
         # reducing along a unique indexed dimension with squeeze=True
         # should raise an error


Test patch:
diff --git a/.github/workflows/testpypi-release.yaml b/.github/workflows/testpypi-release.yaml
index ddc6a2bd..74cb04c1 100644
--- a/.github/workflows/testpypi-release.yaml
+++ b/.github/workflows/testpypi-release.yaml
@@ -78,7 +78,7 @@ jobs:
 
       - name: Publish package to TestPyPI
         if: github.event_name == 'push'
-        uses: pypa/gh-action-pypi-publish@v1.8.5
+        uses: pypa/gh-action-pypi-publish@v1.8.6
         with:
           user: __token__
           password: ${{ secrets.TESTPYPI_TOKEN }}
diff --git a/xarray/tests/test_groupby.py b/xarray/tests/test_groupby.py
index a8530d85..97ba0b58 100644
--- a/xarray/tests/test_groupby.py
+++ b/xarray/tests/test_groupby.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 import datetime
+import operator
 import warnings
 
 import numpy as np
@@ -17,6 +18,7 @@ from xarray.tests import (
     assert_identical,
     create_test_data,
     has_cftime,
+    has_flox,
     has_pandas_version_two,
     requires_dask,
     requires_flox,
@@ -2336,3 +2338,37 @@ def test_groupby_binary_op_regression() -> None:
     anom_gb = x_slice.groupby("time.month") - clim
 
     assert_identical(xr.zeros_like(anom_gb), anom_gb)
+
+
+@requires_flox
+@pytest.mark.parametrize("func", ["sum", "prod"])
+@pytest.mark.parametrize("skipna", [True, False])
+@pytest.mark.parametrize("min_count", [None, 1])
+def test_min_count_vs_flox(func: str, min_count: int | None, skipna: bool) -> None:
+    da = DataArray(
+        data=np.array([np.nan, 1, 1, np.nan, 1, 1]),
+        dims="x",
+        coords={"labels": ("x", np.array([1, 2, 3, 1, 2, 3]))},
+    )
+
+    gb = da.groupby("labels")
+    method = operator.methodcaller(func, min_count=min_count, skipna=skipna)
+    with xr.set_options(use_flox=True):
+        actual = method(gb)
+    with xr.set_options(use_flox=False):
+        expected = method(gb)
+    assert_identical(actual, expected)
+
+
+@pytest.mark.parametrize("use_flox", [True, False])
+def test_min_count_error(use_flox: bool) -> None:
+    if use_flox and not has_flox:
+        pytest.skip()
+    da = DataArray(
+        data=np.array([np.nan, 1, 1, np.nan, 1, 1]),
+        dims="x",
+        coords={"labels": ("x", np.array([1, 2, 3, 1, 2, 3]))},
+    )
+    with xr.set_options(use_flox=use_flox):
+        with pytest.raises(TypeError):
+            da.groupby("labels").mean(min_count=1)