Repository: dask/dask

Issue:
Dask Dataframe - groupby using a list of column names
Per the [documentation](http://dask.pydata.org/en/latest/dataframe-api.html#dask.dataframe.DataFrame.groupby) you should be able to `groupby` using just a "tuple/list of column names".

But it doesn't seem to work. 

``` python
In [4]: dask.__version__
Out[4]: '0.9.0'

In [5]: import dask.dataframe as ddf
   ...: import pandas as pd
   ...: x = pd.DataFrame({'a': [1, 1, 2, 2, 3], 'b': [1, 2, 3, 4, 5]})
   ...: x.groupby(['a']).sum()
   ...: 
   ...: x = ddf.from_pandas(x, chunksize=2)
   ...: x.groupby(['a']).sum().compute()
Traceback (most recent call last):

  File "<ipython-input-5-42f283b0c734>", line 7, in <module>
    x.groupby(['a']).sum().compute()

  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\base.py", line 37, in compute
    return compute(self, **kwargs)[0]

  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\base.py", line 110, in compute
    results = get(dsk, keys, **kwargs)

  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\threaded.py", line 57, in get
    **kwargs)

  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\async.py", line 488, in get_async
    raise(remote_exception(res, tb))

TypeError: unorderable types: list() > int()

Traceback
---------
  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\async.py", line 267, in execute_task
    result = _execute_task(task, data)
  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\async.py", line 249, in _execute_task
    return func(*args2)
  File "C:\Anaconda3\lib\site-packages\dask-0.9.0-py3.5.egg\dask\dataframe\groupby.py", line 211, in <lambda>
    agg = lambda df: aggfunc(df.groupby(level=levels))
  File "C:\Anaconda3\lib\site-packages\pandas\core\generic.py", line 3748, in groupby
    sort=sort, group_keys=group_keys, squeeze=squeeze)
  File "C:\Anaconda3\lib\site-packages\pandas\core\groupby.py", line 1428, in groupby
    return klass(obj, by, **kwds)
  File "C:\Anaconda3\lib\site-packages\pandas\core\groupby.py", line 345, in __init__
    level=level, sort=sort)
  File "C:\Anaconda3\lib\site-packages\pandas\core\groupby.py", line 2320, in _get_grouper
    elif level > 0:
```



Discussion:

Resolving patch:
diff --git a/dask/dataframe/groupby.py b/dask/dataframe/groupby.py
--- a/dask/dataframe/groupby.py
+++ b/dask/dataframe/groupby.py
@@ -9,6 +9,7 @@
                                  aca, map_partitions, no_default)
 from dask.utils import derived_from
 
+
 def _maybe_slice(grouped, columns):
     """
     Slice columns if grouped is pd.DataFrameGroupBy
@@ -18,16 +19,19 @@ def _maybe_slice(grouped, columns):
             return grouped[columns]
     return grouped
 
+
 def _groupby_apply_level0(df, key, func):
     grouped = df.groupby(level=0)
     grouped = _maybe_slice(grouped, key)
     return grouped.apply(func)
 
+
 def _groupby_apply_index(df, ind, key, func):
     grouped = df.groupby(ind)
     grouped = _maybe_slice(grouped, key)
     return grouped.apply(func)
 
+
 def _groupby_get_group(df, by_key, get_key, columns):
     # SeriesGroupBy may pass df which includes group key
     grouped = df.groupby(by_key)
@@ -56,33 +60,37 @@ def _apply_chunk(df, index, func, columns):
     else:
         return func(df.groupby(index)[columns])
 
+
 def _sum(g):
     return g.sum()
 
+
 def _min(g):
     return g.min()
 
+
 def _max(g):
     return g.max()
 
+
 def _count(g):
     return g.count()
 
+
 def _var_chunk(df, index):
     if isinstance(df, pd.Series):
         df = df.to_frame()
     x = df.groupby(index).sum()
     x2 = (df**2).rename(columns=lambda c: c + '-x2')
-    x2 = (pd.concat([df, x2], axis=1)
-            .groupby(index)
-            [[c + '-x2' for c in x.columns]]
-            .sum())
+    cols = [c + '-x2' for c in x.columns]
+    x2 = pd.concat([df, x2], axis=1).groupby(index)[cols].sum()
     n = (df.groupby(index).count()
            .rename(columns=lambda c: c + '-count'))
 
     result = pd.concat([x, x2, n], axis=1)
     return result
 
+
 def _var_agg(g, ddof):
     g = g.groupby(level=0).sum()
     nc = len(g.columns)
@@ -98,6 +106,7 @@ def _var_agg(g, ddof):
     assert isinstance(result, pd.DataFrame)
     return result
 
+
 ###############################################################
 # nunique
 ###############################################################
@@ -109,6 +118,7 @@ def _nunique_df_chunk(df, index):
     grouped.index = grouped.index.get_level_values(level=0)
     return grouped
 
+
 def _nunique_series_chunk(df, index):
     assert isinstance(df, pd.Series)
     if isinstance(index, np.ndarray):
@@ -138,9 +148,9 @@ def __init__(self, df, index=None, slice=None, **kwargs):
 
         # grouping key passed via groupby method
         if (isinstance(index, (DataFrame, Series, Index)) and
-            isinstance(df, DataFrame) and
-            index._name.startswith(df._name) and
-            index._name[len(df._name) + 1:] in df.columns):
+                isinstance(df, DataFrame) and
+                index._name.startswith(df._name) and
+                index._name[len(df._name) + 1:] in df.columns):
             index = index._name[len(df._name) + 1:]
         self.index = index
 
@@ -175,7 +185,7 @@ def _is_grouped_by_sliced_column(self, df, index):
         """
         if isinstance(df, DataFrame) and isinstance(index, Series):
             if (index.name is not None and
-                index._name == df._name + '.' + index.name):
+                    index._name == df._name + '.' + index.name):
                 return True
         return False
 
@@ -203,7 +213,7 @@ def _aca_agg(self, token, func, aggfunc=None):
 
         token = self._token_prefix + token
 
-        if isinstance(self.index, list):
+        if isinstance(self.index, (tuple, list)) and len(self.index) > 1:
             levels = list(range(len(self.index)))
         else:
             levels = 0


Test patch:
diff --git a/dask/dataframe/tests/test_groupby.py b/dask/dataframe/tests/test_groupby.py
--- a/dask/dataframe/tests/test_groupby.py
+++ b/dask/dataframe/tests/test_groupby.py
@@ -161,6 +161,22 @@ def test_groupby_multilevel_getitem():
         assert eq(d.mean(), p.mean().astype(float))
 
 
+def test_groupby_multilevel_agg():
+    df = pd.DataFrame({'a': [1, 2, 3, 1, 2, 3],
+                       'b': [1, 2, 1, 4, 2, 1],
+                       'c': [1, 3, 2, 1, 1, 2],
+                       'd': [1, 2, 1, 1, 2, 2]})
+    ddf = dd.from_pandas(df, 2)
+
+    sol = df.groupby(['a']).mean()
+    res = ddf.groupby(['a']).mean()
+    assert eq(res, sol)
+
+    sol = df.groupby(['a', 'c']).mean()
+    res = ddf.groupby(['a', 'c']).mean()
+    assert eq(res, sol)
+
+
 def test_groupby_get_group():
     dsk = {('x', 0): pd.DataFrame({'a': [1, 2, 6], 'b': [4, 2, 7]},
                                   index=[0, 1, 3]),