Repository: pandas-dev/pandas

Issue:
[API/ERR]: Better error message on unstack with non-unique index names
``` python
df = pd.DataFrame([1,1], index=pd.MultiIndex.from_tuples([('a', 'a'), ('a', 'b')], names=['i1', 'i1']))

In [5]: df.unstack('i1')

...

KeyError: 'Level i1 not found'
```

I can do a PR later.

Should also probably be a `ValueError` instead of a `KeyError`.



Discussion:

Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -2317,6 +2317,13 @@ def _set_names(self, values, validate=True):
     names = property(
         fset=_set_names, fget=_get_names, doc="Names of levels in MultiIndex")
 
+    def _reference_duplicate_name(self, name):
+        """
+        Returns True if the name refered to in self.names is duplicated.
+        """
+        # count the times name equals an element in self.names.
+        return np.sum(name == np.asarray(self.names)) > 1
+
     def _format_native_types(self, **kwargs):
         return self.tolist()
 
diff --git a/pandas/core/reshape.py b/pandas/core/reshape.py
--- a/pandas/core/reshape.py
+++ b/pandas/core/reshape.py
@@ -1,6 +1,5 @@
 # pylint: disable=E1101,E1103
 # pylint: disable=W0703,W0622,W0613,W0201
-
 from pandas.compat import range, zip
 from pandas import compat
 import itertools
@@ -69,6 +68,13 @@ def __init__(self, values, index, level=-1, value_columns=None):
             raise ValueError('must pass column labels for multi-column data')
 
         self.index = index
+
+        if isinstance(self.index, MultiIndex):
+            if index._reference_duplicate_name(level):
+                msg = ("Ambiguous reference to {0}. The index "
+                       "names are not unique.".format(level))
+                raise ValueError(msg)
+
         self.level = self.index._get_level_number(level)
 
         levels = index.levels
@@ -497,6 +503,12 @@ def stack(frame, level=-1, dropna=True):
     stacked : Series
     """
     N, K = frame.shape
+    if isinstance(frame.columns, MultiIndex):
+        if frame.columns._reference_duplicate_name(level):
+            msg = ("Ambiguous reference to {0}. The column "
+                   "names are not unique.".format(level))
+            raise ValueError(msg)
+
     if isinstance(level, int) and level < 0:
         level += frame.columns.nlevels
 


Test patch:
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -11387,6 +11387,16 @@ def test_unstack_dtypes(self):
         expected = Series({'float64' : 2, 'object' : 2})
         assert_series_equal(result, expected)
 
+    def test_unstack_non_unique_index_names(self):
+        idx = MultiIndex.from_tuples([('a', 'b'), ('c', 'd')],
+                                     names=['c1', 'c1'])
+        df = DataFrame([1, 2], index=idx)
+        with tm.assertRaises(ValueError):
+            df.unstack('c1')
+
+        with tm.assertRaises(ValueError):
+            df.T.stack('c1')
+
     def test_reset_index(self):
         stacked = self.frame.stack()[::2]
         stacked = DataFrame({'foo': stacked, 'bar': stacked})
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
@@ -1534,6 +1534,13 @@ def test_names(self):
         level_names = [level.name for level in index.levels]
         self.assertEqual(ind_names, level_names)
 
+    def test_reference_duplicate_name(self):
+        idx = MultiIndex.from_tuples([('a', 'b'), ('c', 'd')], names=['x', 'x'])
+        self.assertTrue(idx._reference_duplicate_name('x'))
+
+        idx = MultiIndex.from_tuples([('a', 'b'), ('c', 'd')], names=['x', 'y'])
+        self.assertFalse(idx._reference_duplicate_name('x'))
+
     def test_astype(self):
         expected = self.index.copy()
         actual = self.index.astype('O')