Repository: pandas-dev/pandas

Issue:
BUG: groupby over a CategoricalIndex in axis=1
#### Code Sample
```python
from pandas import DataFrame, CategoricalIndex

cat_index = CategoricalIndex(['a', 'b', 'a', 'b'], categories=['a', 'b'])
df = DataFrame(data=1.0, index=[0, 1], columns=cat_index)
print(df.groupby(axis=1, level=0).sum())
# Attempting a groupby using a CategoricalIndex results in:
#     ValueError: Categorical dtype grouper must have len(grouper) == len(data)
```
#### Problem description
Attempting a groupby over a CategoricalIndex for the columns results in a ValueError when using Pandas 0.21.0
#### Expected Output
```
     a    b
0  2.0  2.0
1  2.0  2.0
```
#### Output of ``pd.show_versions()``
<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.5.4.final.0
python-bits: 64
OS: Linux
OS-release: 2.6.32-696.10.3.el6.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US
LOCALE: en_US.ISO8859-1
pandas: 0.21.0
pytest: None
pip: 9.0.1
setuptools: 36.7.2
Cython: None
numpy: 1.13.3
scipy: 1.0.0
pyarrow: 0.7.1
xarray: None
IPython: 6.2.1
sphinx: 1.6.5
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.3
blosc: None
bottleneck: 1.2.1
tables: 3.4.2
numexpr: 2.6.4
feather: None
matplotlib: 2.0.2
openpyxl: 2.5.0b1
xlrd: 1.0.0
xlwt: 1.3.0
xlsxwriter: None
lxml: None
bs4: None
html5lib: 0.999999999
sqlalchemy: 1.1.6
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
</details>



Discussion:
This works on the transpose.

```
In [60]: df.T.groupby(level=0).sum()
Out[60]: 
     0    1
a  2.0  2.0
b  2.0  2.0
```

a pull-request to fix would be great @ekisslinger 

Resolving patch:
diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py
--- a/pandas/core/groupby.py
+++ b/pandas/core/groupby.py
@@ -2859,9 +2859,11 @@ def is_in_obj(gpr):
         else:
             in_axis, name = False, None
 
-        if is_categorical_dtype(gpr) and len(gpr) != len(obj):
-            raise ValueError("Categorical dtype grouper must "
-                             "have len(grouper) == len(data)")
+        if is_categorical_dtype(gpr) and len(gpr) != obj.shape[axis]:
+            raise ValueError(
+                ("Length of grouper ({len_gpr}) and axis ({len_axis})"
+                 " must be same length"
+                 .format(len_gpr=len(gpr), len_axis=obj.shape[axis])))
 
         # create the Grouping
         # allow us to passing the actual Grouping as the gpr


Test patch:
diff --git a/pandas/tests/groupby/test_grouping.py b/pandas/tests/groupby/test_grouping.py
--- a/pandas/tests/groupby/test_grouping.py
+++ b/pandas/tests/groupby/test_grouping.py
@@ -6,7 +6,7 @@
 
 from warnings import catch_warnings
 from pandas import (date_range, Timestamp,
-                    Index, MultiIndex, DataFrame, Series)
+                    Index, MultiIndex, DataFrame, Series, CategoricalIndex)
 from pandas.util.testing import (assert_panel_equal, assert_frame_equal,
                                  assert_series_equal, assert_almost_equal)
 from pandas.compat import lrange, long
@@ -251,6 +251,29 @@ def test_groupby_levels_and_columns(self):
         by_columns.columns = pd.Index(by_columns.columns, dtype=np.int64)
         tm.assert_frame_equal(by_levels, by_columns)
 
+    def test_groupby_categorical_index_and_columns(self):
+        # GH18432
+        columns = ['A', 'B', 'A', 'B']
+        categories = ['B', 'A']
+        data = np.ones((5, 4), int)
+        cat_columns = CategoricalIndex(columns,
+                                       categories=categories,
+                                       ordered=True)
+        df = DataFrame(data=data, columns=cat_columns)
+        result = df.groupby(axis=1, level=0).sum()
+        expected_data = 2 * np.ones((5, 2), int)
+        expected_columns = CategoricalIndex(categories,
+                                            categories=categories,
+                                            ordered=True)
+        expected = DataFrame(data=expected_data, columns=expected_columns)
+        assert_frame_equal(result, expected)
+
+        # test transposed version
+        df = DataFrame(data.T, index=cat_columns)
+        result = df.groupby(axis=0, level=0).sum()
+        expected = DataFrame(data=expected_data.T, index=expected_columns)
+        assert_frame_equal(result, expected)
+
     def test_grouper_getting_correct_binner(self):
 
         # GH 10063