Repository: pandas-dev/pandas

Issue:
BUG/ENH: groupby.rolling.agg / Column(s) low already selected
#### GroupBy.rolling.agg failed with Column(s) low already selected

```python
data = pd.DataFrame({"stock": [1, 1, 1, 2, 2, 2, 2], "low": [10, 20, 30, 10, 30, 40, 80]})
data.set_index("stock", inplace=True)
# failed
data.groupby(level="stock").rolling(2).agg({"low": {"mean": "mean", "max": "max"}})
# works
data.rolling(2).agg({"low": {"mean": "mean", "max": "max"}})
```

This also hits `groupby().resample.agg`


```python
In [13]: df = pd.DataFrame({"A": pd.to_datetime(['2015', '2017']), "B": [1, 1]})

In [14]: df
Out[14]:
           A  B
0 2015-01-01  1
1 2017-01-01  1

In [15]: df.set_index("A").groupby([0, 0]).resample("AS")
Out[15]: DatetimeIndexResamplerGroupby [freq=<YearBegin: month=1>, axis=0, closed=left, label=left, convention=e, base=0]

In [16]: df.set_index("A").groupby([0, 0]).resample("AS").agg(['sum', 'count'])
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-16-5f1c18a8d4ac> in <module>()
----> 1 df.set_index("A").groupby([0, 0]).resample("AS").agg(['sum', 'count'])

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/resample.py in aggregate(self, arg, *args, **kwargs)
    339
    340         self._set_binner()
--> 341         result, how = self._aggregate(arg, *args, **kwargs)
    342         if result is None:
    343             result = self._groupby_and_aggregate(arg,

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in _aggregate(self, arg, *args, **kwargs)
    538             return self._aggregate_multiple_funcs(arg,
    539                                                   _level=_level,
--> 540                                                   _axis=_axis), None
    541         else:
    542             result = None

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in _aggregate_multiple_funcs(self, arg, _level, _axis)
    583                 try:
    584                     colg = self._gotitem(col, ndim=1, subset=obj[col])
--> 585                     results.append(colg.aggregate(arg))
    586                     keys.append(col)
    587                 except (TypeError, DataError):

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/resample.py in aggregate(self, arg, *args, **kwargs)
    339
    340         self._set_binner()
--> 341         result, how = self._aggregate(arg, *args, **kwargs)
    342         if result is None:
    343             result = self._groupby_and_aggregate(arg,

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in _aggregate(self, arg, *args, **kwargs)
    538             return self._aggregate_multiple_funcs(arg,
    539                                                   _level=_level,
--> 540                                                   _axis=_axis), None
    541         else:
    542             result = None

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in _aggregate_multiple_funcs(self, arg, _level, _axis)
    582             for col in obj:
    583                 try:
--> 584                     colg = self._gotitem(col, ndim=1, subset=obj[col])
    585                     results.append(colg.aggregate(arg))
    586                     keys.append(col)

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in _gotitem(self, key, ndim, subset)
    675                        for attr in self._attributes])
    676         self = self.__class__(subset,
--> 677                               groupby=self._groupby[key],
    678                               parent=self,
    679                               **kwargs)

~/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/base.py in __getitem__(self, key)
    241         if self._selection is not None:
    242             raise Exception('Column(s) {selection} already selected'
--> 243                             .format(selection=self._selection))
    244
    245         if isinstance(key, (list, tuple, ABCSeries, ABCIndexClass,

Exception: Column(s) B already selected
```

#### Problem description

It's not work as expected

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Linux
OS-release: 4.4.0-53-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.19.2+0.g825876c.dirty
nose: 1.3.7
pip: 9.0.1
setuptools: 27.2.0
Cython: 0.24.1
numpy: 1.11.1
scipy: 0.18.1
statsmodels: 0.6.1
xarray: None
IPython: 5.1.0
sphinx: 1.4.6
patsy: 0.4.1
dateutil: 2.5.3
pytz: 2016.6.1
blosc: None
bottleneck: 1.1.0
tables: 3.2.3.1
numexpr: 2.6.1
matplotlib: 1.5.3
openpyxl: 2.3.2
xlrd: 1.0.0
xlwt: 1.1.2
xlsxwriter: 0.9.3
lxml: 3.6.4
bs4: 4.5.1
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: 1.0.13
pymysql: 0.7.9.None
psycopg2: None
jinja2: 2.8
boto: 2.42.0
pandas_datareader: None
</details>



Discussion:
sophisticated ``.agg`` was never explicitly implemented on ``.groupby.rolling``, so not surprising this doesn't work. if you'd like to debug please do.

```
In [12]: data.groupby(level="stock").rolling(2).mean()
Out[12]:
              low
stock stock
1     1       NaN
      1      15.0
      1      25.0
2     2       NaN
      2      20.0
      2      35.0
      2      60.0

In [13]: data.groupby(level="stock").rolling(2).max()
Out[13]:
              low
stock stock
1     1       NaN
      1      20.0
      1      30.0
2     2       NaN
      2      30.0
      2      40.0
      2      80.0
```
Hi All!

I've faced with the same error: https://stackoverflow.com/questions/46350611/aggregate-group-by-rolling-window-keyerror-column-not-found-foo

Resolving patch:
diff --git a/pandas/core/base.py b/pandas/core/base.py
--- a/pandas/core/base.py
+++ b/pandas/core/base.py
@@ -245,8 +245,8 @@ def _obj_with_exclusions(self):
 
     def __getitem__(self, key):
         if self._selection is not None:
-            raise Exception('Column(s) {selection} already selected'
-                            .format(selection=self._selection))
+            raise IndexError('Column(s) {selection} already selected'
+                             .format(selection=self._selection))
 
         if isinstance(key, (list, tuple, ABCSeries, ABCIndexClass,
                             np.ndarray)):
diff --git a/pandas/core/groupby/base.py b/pandas/core/groupby/base.py
--- a/pandas/core/groupby/base.py
+++ b/pandas/core/groupby/base.py
@@ -44,8 +44,15 @@ def _gotitem(self, key, ndim, subset=None):
         # we need to make a shallow copy of ourselves
         # with the same groupby
         kwargs = {attr: getattr(self, attr) for attr in self._attributes}
+
+        # Try to select from a DataFrame, falling back to a Series
+        try:
+            groupby = self._groupby[key]
+        except IndexError:
+            groupby = self._groupby
+
         self = self.__class__(subset,
-                              groupby=self._groupby[key],
+                              groupby=groupby,
                               parent=self,
                               **kwargs)
         self._reset_cache()


Test patch:
diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py
--- a/pandas/tests/groupby/test_groupby.py
+++ b/pandas/tests/groupby/test_groupby.py
@@ -623,8 +623,14 @@ def test_as_index_series_return_frame(df):
     assert isinstance(result2, DataFrame)
     assert_frame_equal(result2, expected2)
 
-    # corner case
-    pytest.raises(Exception, grouped['C'].__getitem__, 'D')
+
+def test_as_index_series_column_slice_raises(df):
+    # GH15072
+    grouped = df.groupby('A', as_index=False)
+    msg = r"Column\(s\) C already selected"
+
+    with tm.assert_raises_regex(IndexError, msg):
+        grouped['C'].__getitem__('D')
 
 
 def test_groupby_as_index_cython(df):
diff --git a/pandas/tests/test_window.py b/pandas/tests/test_window.py
--- a/pandas/tests/test_window.py
+++ b/pandas/tests/test_window.py
@@ -1,3 +1,4 @@
+from collections import OrderedDict
 from itertools import product
 import pytest
 import warnings
@@ -314,6 +315,53 @@ def test_preserve_metadata(self):
         assert s2.name == 'foo'
         assert s3.name == 'foo'
 
+    @pytest.mark.parametrize("func,window_size,expected_vals", [
+        ('rolling', 2, [[np.nan, np.nan, np.nan, np.nan],
+                        [15., 20., 25., 20.],
+                        [25., 30., 35., 30.],
+                        [np.nan, np.nan, np.nan, np.nan],
+                        [20., 30., 35., 30.],
+                        [35., 40., 60., 40.],
+                        [60., 80., 85., 80]]),
+        ('expanding', None, [[10., 10., 20., 20.],
+                             [15., 20., 25., 20.],
+                             [20., 30., 30., 20.],
+                             [10., 10., 30., 30.],
+                             [20., 30., 35., 30.],
+                             [26.666667, 40., 50., 30.],
+                             [40., 80., 60., 30.]])])
+    def test_multiple_agg_funcs(self, func, window_size, expected_vals):
+        # GH 15072
+        df = pd.DataFrame([
+            ['A', 10, 20],
+            ['A', 20, 30],
+            ['A', 30, 40],
+            ['B', 10, 30],
+            ['B', 30, 40],
+            ['B', 40, 80],
+            ['B', 80, 90]], columns=['stock', 'low', 'high'])
+
+        f = getattr(df.groupby('stock'), func)
+        if window_size:
+            window = f(window_size)
+        else:
+            window = f()
+
+        index = pd.MultiIndex.from_tuples([
+            ('A', 0), ('A', 1), ('A', 2),
+            ('B', 3), ('B', 4), ('B', 5), ('B', 6)], names=['stock', None])
+        columns = pd.MultiIndex.from_tuples([
+            ('low', 'mean'), ('low', 'max'), ('high', 'mean'),
+            ('high', 'min')])
+        expected = pd.DataFrame(expected_vals, index=index, columns=columns)
+
+        result = window.agg(OrderedDict((
+            ('low', ['mean', 'max']),
+            ('high', ['mean', 'min']),
+        )))
+
+        tm.assert_frame_equal(result, expected)
+
 
 @pytest.mark.filterwarnings("ignore:can't resolve package:ImportWarning")
 class TestWindow(Base):