Repository: pandas-dev/pandas

Issue:
GroupBy on a MultiIndex with the index causes python to stop working
If you try following piece of code, my python process crashes:

```
df = pd.DataFrame({'A':[1,1,2], 'B':[1,2,1], 'C':[1,2,3]})
df = df.set_index(['A', 'B'])
df.index.groupby(df.index)
```

I know this code is as such probably bullshit (I also did it by accident, the first index shouldn't have been there :-)), but still it should then generate a Error Message instead of causing python to stop?

BTW `pd.Index.groupby` is a publicly available, but not really clear what is does or is meant for.



Discussion:
`Index` methods aren't really public as a rule - they aren't explained in the docs and usually they are just delegated to on certain methods. In the same vein, `groupby` is only nominally public on Index (as in, it's not really public and you shouldn't be using it).

This crashes because `MultiIndex` isn't a real ndarray and that method expects to get an ndarray of values.

If you feel strongly this should work, just call `_values_from_object` in core/common on the object in the `groupby` method before passing to `_groupby`.

No, I don't feel strongly at all that this should work, it just shouldn't crash my python process because I mistyped something (it wasn't on purpose that I tried to groupby on the index). And that's why I made an issue about it.

The method _is_ public in the sense that a user sees it when doing tab completion, and that index methods are included in the api docs.

is this groupby actually called internally anywhere? if not should remove it - I believe that this was added a while back when groupby was in flux 

maybe convert this issue to provide more validation in groupby itself

@jreback I think it does get used somewhere -- causes some weird failures about not having the `groups` property.  `Index.groubpy` is definitely an internal method, but given that it just dispatches to the appropriate `algos.pyx` method, maybe we could just remove it and have the `GroupBy` objects call it themselves.

@jorisvandenbossche this actually works now (probably because of one of the many changes to indexing internals). do you want to close this?

this returns a dict now; it is used 'internally' in groupby. 

@jtratner want to do some tests, just proving that this doesn't crash; then can close?

@jtratner ah good you notice it. But before closing, I would:
- or just add a test this does not break again
- or check if it is used somewhere internally and if not remove it? (or if used only a few times, just directly call the underlying function)

@jreback ah, didn't see your comment yet. I will just add some tests then.

@jorisvandenbossche np

`Index.groupby` is public, meant to be inheritied if an index does need a custom way to do provide groups.


Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -1555,6 +1555,20 @@ def _possibly_promote(self, other):
         return self, other
 
     def groupby(self, to_groupby):
+        """
+        Group the index labels by a given array of values.
+
+        Parameters
+        ----------
+        to_groupby : array
+            Values used to determine the groups.
+
+        Returns
+        -------
+        groups : dict
+            {group name -> group labels}
+
+        """
         return self._groupby(self.values, _values_from_object(to_groupby))
 
     def map(self, mapper):


Test patch:
diff --git a/pandas/tests/test_index.py b/pandas/tests/test_index.py
--- a/pandas/tests/test_index.py
+++ b/pandas/tests/test_index.py
@@ -1137,6 +1137,11 @@ def get_reindex_type(target):
         self.assertEqual(reindexed.levels[0].dtype.type, np.int64)
         self.assertEqual(reindexed.levels[1].dtype.type, np.float64)
 
+    def test_groupby(self):
+        idx = Index(range(5))
+        groups = idx.groupby(np.array([1,1,2,2,2]))
+        exp = {1: [0, 1], 2: [2, 3, 4]}
+        tm.assert_dict_equal(groups, exp)
 
 
 class Numeric(Base):
@@ -3524,6 +3529,17 @@ def test_reindex_lvl_preserves_type_if_target_is_empty_list_or_array(self):
         self.assertEqual(idx.reindex([], level=1)[0].levels[1].dtype.type,
                          np.object_)
 
+    def test_groupby(self):
+        groups = self.index.groupby(np.array([1, 1, 1, 2, 2, 2]))
+        labels = self.index.get_values().tolist()
+        exp = {1: labels[:3], 2: labels[3:]}
+        tm.assert_dict_equal(groups, exp)
+
+        # GH5620
+        groups = self.index.groupby(self.index)
+        exp = dict((key, [key]) for key in self.index)
+        tm.assert_dict_equal(groups, exp)
+
 
 def test_get_combined_index():
     from pandas.core.index import _get_combined_index