Repository: dask/dask

Issue:
Aggregate functions on DataFrames/Series with empty partitions lead to ValueError
Dask 0.15.1

Example:

```python
import dask
from dask import dataframe as dd
import pandas as pd

(dd.from_delayed([dask.delayed(pd.DataFrame)({'a': i})
                 for i in [[1, 2, 3], []]])
 .idxmax()
 .compute())
```

Executing it results in:
```
ValueError: attempt to get argmax of an empty sequence
```

A workaround would be to use `map_partitions` and then to aggregate the result one more time.



Discussion:

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -3737,23 +3737,31 @@ def _reduction_aggregate(x, aca_aggregate=None, **kwargs):
 
 
 def idxmaxmin_chunk(x, fn=None, skipna=True):
-    idx = getattr(x, fn)(skipna=skipna)
     minmax = 'max' if fn == 'idxmax' else 'min'
-    value = getattr(x, minmax)(skipna=skipna)
-    if isinstance(x, pd.DataFrame):
+    if len(x) > 0:
+        idx = getattr(x, fn)(skipna=skipna)
+        value = getattr(x, minmax)(skipna=skipna)
+    else:
+        idx = value = pd.Series([], dtype='i8')
+    if isinstance(idx, pd.Series):
         return pd.DataFrame({'idx': idx, 'value': value})
     return pd.DataFrame({'idx': [idx], 'value': [value]})
 
 
 def idxmaxmin_row(x, fn=None, skipna=True):
-    x = x.set_index('idx')
-    idx = getattr(x.value, fn)(skipna=skipna)
     minmax = 'max' if fn == 'idxmax' else 'min'
-    value = getattr(x.value, minmax)(skipna=skipna)
-    return pd.DataFrame({'idx': [idx], 'value': [value]})
+    if len(x) > 0:
+        x = x.set_index('idx')
+        idx = [getattr(x.value, fn)(skipna=skipna)]
+        value = [getattr(x.value, minmax)(skipna=skipna)]
+    else:
+        idx = value = pd.Series([], dtype='i8')
+    return pd.DataFrame({'idx': idx, 'value': value})
 
 
 def idxmaxmin_combine(x, fn=None, skipna=True):
+    if len(x) == 0:
+        return x
     return (x.groupby(level=0)
              .apply(idxmaxmin_row, fn=fn, skipna=skipna)
              .reset_index(level=1, drop=True))
@@ -3761,6 +3769,8 @@ def idxmaxmin_combine(x, fn=None, skipna=True):
 
 def idxmaxmin_agg(x, fn=None, skipna=True, scalar=False):
     res = idxmaxmin_combine(x, fn, skipna=skipna)['idx']
+    if len(res) == 0:
+        raise ValueError("attempt to get argmax of an empty sequence")
     if scalar:
         return res[0]
     res.name = None


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -2298,6 +2298,33 @@ def test_idxmaxmin(idx, skipna):
             ddf.a.idxmin(skipna=skipna, split_every=2)._name)
 
 
+def test_idxmaxmin_empty_partitions():
+    df = pd.DataFrame({'a': [1, 2, 3],
+                       'b': [1.5, 2, 3],
+                       'c': [np.NaN] * 3,
+                       'd': [1, 2, np.NaN]})
+    empty = df.iloc[:0]
+
+    ddf = dd.concat([dd.from_pandas(df, npartitions=1)] +
+                    [dd.from_pandas(empty, npartitions=1)] * 10)
+
+    for skipna in [True, False]:
+        assert_eq(ddf.idxmin(skipna=skipna, split_every=3),
+                  df.idxmin(skipna=skipna))
+
+    assert_eq(ddf[['a', 'b', 'd']].idxmin(skipna=skipna, split_every=3),
+              df[['a', 'b', 'd']].idxmin(skipna=skipna))
+
+    assert_eq(ddf.b.idxmax(split_every=3), df.b.idxmax())
+
+    # Completely empty raises
+    ddf = dd.concat([dd.from_pandas(empty, npartitions=1)] * 10)
+    with pytest.raises(ValueError):
+        ddf.idxmax().compute()
+    with pytest.raises(ValueError):
+        ddf.b.idxmax().compute()
+
+
 def test_getitem_meta():
     data = {'col1': ['a', 'a', 'b'],
             'col2': [0, 1, 0]}