Repository: pandas-dev/pandas

Issue:
ERR: improve error message with unalignable label indexing
#### A small, complete example of the issue

Using a boolean index in a `pandas.Series` on a data frame with an string index fails with `Unalignable boolean Series key provided`:

``` python
import pandas as pd

# DF without string index
df = pd.DataFrame({"A": [0, 1, 2]})
df.loc[[True, True, True], :]               # works
df.loc[pd.Series([True, True, True]), :]    # works

# DF with string index
df = pd.DataFrame({"A": [0, 1, 2]}, index=["a", "b", "c"])
df.loc[[True, True, True], :]               # works
df.loc[pd.Series([True, True, True]), :]    # fails
```
#### Expected Output

Boolean indexing should work with a `pandas.Series` like it does with regular Python lists and boolean numpy arrays.
#### Output of `pd.show_versions()`

<details>
## INSTALLED VERSIONS

commit: None
python: 2.7.6.final.0
python-bits: 64
OS: Linux
OS-release: 3.13.0-98-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

pandas: 0.18.1
nose: None
pip: 8.1.2
setuptools: 27.2.0
Cython: 0.24.1
numpy: 1.11.1
scipy: 0.18.0
statsmodels: None
xarray: None
IPython: 5.1.0
sphinx: None
patsy: None
dateutil: 2.5.3
pytz: 2016.6.1
blosc: None
bottleneck: 1.1.0
tables: None
numexpr: 2.6.1
matplotlib: 1.5.3
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: None
boto: None
pandas_datareader: None
</details>



Discussion:
I think the error message is rather clear: "Unalignable boolean Series key provided" -> the index of the boolean series is not alignable with the index of the DataFrame you are trying to index. Lists or arrays don't have an index, so there is not an issue. If you want to ignore the index of the series, you can use `s.values` to index.

Not sure if this is documented somewhere thoroughly.

Understood, but then it is mainly the error message that is very confusing: A series of length 3 _is_ alignable intuitively with a length-3-dataframe, it is just not what Pandas considers alignable.

Ah yes, it is unalignable on the _labels_ of the indices (although the length matches). That is what the term 'alignment' means in pandas-world. 
But we would maybe add in the error massage some more explanation.

ok will mark this for error message improvement @bluenote10 if you'd like to submit a PR.

I suppose you could mention that this is _label_ alignment would be enough.


Resolving patch:
diff --git a/pandas/core/common.py b/pandas/core/common.py
--- a/pandas/core/common.py
+++ b/pandas/core/common.py
@@ -97,6 +97,16 @@ class UnsupportedFunctionCall(ValueError):
     pass
 
 
+class UnsortedIndexError(KeyError):
+    """ Error raised when attempting to get a slice of a MultiIndex
+    and the index has not been lexsorted. Subclass of `KeyError`.
+
+    .. versionadded:: 0.20.0
+
+    """
+    pass
+
+
 class AbstractMethodError(NotImplementedError):
     """Raise this error instead of NotImplementedError for abstract methods
     while keeping compatibility with Python 2 and Python 3.
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -1814,7 +1814,9 @@ def check_bool_indexer(ax, key):
         result = result.reindex(ax)
         mask = isnull(result._values)
         if mask.any():
-            raise IndexingError('Unalignable boolean Series key provided')
+            raise IndexingError('Unalignable boolean Series provided as '
+                                'indexer (index of the boolean Series and of '
+                                'the indexed object do not match')
         result = result.astype(bool)._values
     elif is_sparse(result):
         result = result.to_dense()
diff --git a/pandas/indexes/multi.py b/pandas/indexes/multi.py
--- a/pandas/indexes/multi.py
+++ b/pandas/indexes/multi.py
@@ -25,7 +25,8 @@
 from pandas.core.common import (_values_from_object,
                                 is_bool_indexer,
                                 is_null_slice,
-                                PerformanceWarning)
+                                PerformanceWarning,
+                                UnsortedIndexError)
 
 
 from pandas.core.base import FrozenList
@@ -1936,9 +1937,10 @@ def get_locs(self, tup):
 
         # must be lexsorted to at least as many levels
         if not self.is_lexsorted_for_tuple(tup):
-            raise KeyError('MultiIndex Slicing requires the index to be fully '
-                           'lexsorted tuple len ({0}), lexsort depth '
-                           '({1})'.format(len(tup), self.lexsort_depth))
+            raise UnsortedIndexError('MultiIndex Slicing requires the index '
+                                     'to be fully lexsorted tuple len ({0}), '
+                                     'lexsort depth ({1})'
+                                     .format(len(tup), self.lexsort_depth))
 
         # indexer
         # this is the list of all values that we want to select


Test patch:
diff --git a/pandas/tests/indexes/test_multi.py b/pandas/tests/indexes/test_multi.py
--- a/pandas/tests/indexes/test_multi.py
+++ b/pandas/tests/indexes/test_multi.py
@@ -8,7 +8,7 @@
 
 from pandas import (DataFrame, date_range, period_range, MultiIndex, Index,
                     CategoricalIndex, compat)
-from pandas.core.common import PerformanceWarning
+from pandas.core.common import PerformanceWarning, UnsortedIndexError
 from pandas.indexes.base import InvalidIndexError
 from pandas.compat import range, lrange, u, PY3, long, lzip
 
@@ -2535,3 +2535,19 @@ def test_dropna(self):
         msg = "invalid how option: xxx"
         with tm.assertRaisesRegexp(ValueError, msg):
             idx.dropna(how='xxx')
+
+    def test_unsortedindex(self):
+        # GH 11897
+        mi = pd.MultiIndex.from_tuples([('z', 'a'), ('x', 'a'), ('y', 'b'),
+                                        ('x', 'b'), ('y', 'a'), ('z', 'b')],
+                                       names=['one', 'two'])
+        df = pd.DataFrame([[i, 10 * i] for i in lrange(6)], index=mi,
+                          columns=['one', 'two'])
+
+        with assertRaises(UnsortedIndexError):
+            df.loc(axis=0)['z', :]
+        df.sort_index(inplace=True)
+        self.assertEqual(len(df.loc(axis=0)['z', :]), 2)
+
+        with assertRaises(KeyError):
+            df.loc(axis=0)['q', :]
diff --git a/pandas/tests/indexing/test_indexing.py b/pandas/tests/indexing/test_indexing.py
--- a/pandas/tests/indexing/test_indexing.py
+++ b/pandas/tests/indexing/test_indexing.py
@@ -23,7 +23,7 @@
                              MultiIndex, Timestamp, Timedelta)
 from pandas.formats.printing import pprint_thing
 from pandas import concat
-from pandas.core.common import PerformanceWarning
+from pandas.core.common import PerformanceWarning, UnsortedIndexError
 
 import pandas.util.testing as tm
 from pandas import date_range
@@ -2230,7 +2230,7 @@ def f():
         df = df.sortlevel(level=1, axis=0)
         self.assertEqual(df.index.lexsort_depth, 0)
         with tm.assertRaisesRegexp(
-                KeyError,
+                UnsortedIndexError,
                 'MultiIndex Slicing requires the index to be fully '
                 r'lexsorted tuple len \(2\), lexsort depth \(0\)'):
             df.loc[(slice(None), df.loc[:, ('a', 'bar')] > 5), :]
@@ -2417,7 +2417,7 @@ def test_per_axis_per_level_doc_examples(self):
         def f():
             df.loc['A1', (slice(None), 'foo')]
 
-        self.assertRaises(KeyError, f)
+        self.assertRaises(UnsortedIndexError, f)
         df = df.sortlevel(axis=1)
 
         # slicing
@@ -3480,8 +3480,12 @@ def test_iloc_mask(self):
             ('index', '.loc'): '0b11',
             ('index', '.iloc'): ('iLocation based boolean indexing '
                                  'cannot use an indexable as a mask'),
-            ('locs', ''): 'Unalignable boolean Series key provided',
-            ('locs', '.loc'): 'Unalignable boolean Series key provided',
+            ('locs', ''): 'Unalignable boolean Series provided as indexer '
+                          '(index of the boolean Series and of the indexed '
+                          'object do not match',
+            ('locs', '.loc'): 'Unalignable boolean Series provided as indexer '
+                              '(index of the boolean Series and of the '
+                              'indexed object do not match',
             ('locs', '.iloc'): ('iLocation based boolean indexing on an '
                                 'integer type is not available'),
         }