Repository: pandas-dev/pandas

Issue:
BUG: MultiIndex .loc fails with all numpy arrays
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd
import numpy as np

a = [10,20,30]
b = [1,2,3]
index = pd.MultiIndex.from_product([a,b])
df = pd.DataFrame(np.arange(len(index)), index=index, columns=['Data'])

slice1 = df.loc[([10,20],            [2,3]),           'Data']
slice2 = df.loc[(np.array([10,20]),  [2,3]),           'Data']
slice3 = df.loc[([10,20],            np.array([2,3])), 'Data']
slice4 = df.loc[(np.array([10,20]),  np.array([2,3])), 'Data']

```
#### Problem description

slice4 in the sample fails with TypeError: '(array([10, 20]), array([2, 3]))' is an invalid key.
slice1-3 all work as expected.


#### Expected Output

expect slicing to work with numpy arrays the same as lists.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 45 Stepping 7, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None

pandas: 0.22.0
pytest: 2.9.2
pip: 8.1.2
setuptools: 27.2.0
Cython: 0.24.1
numpy: 1.14.0
scipy: 1.0.0
pyarrow: None
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.2.1
tables: 3.4.2
numexpr: 2.6.4
feather: None
matplotlib: 2.1.2
openpyxl: 2.3.2
xlrd: None
xlwt: 1.1.2
xlsxwriter: 0.9.3
lxml: 3.6.4
bs4: 4.5.1
html5lib: 0.9999999
sqlalchemy: 1.0.13
pymysql: None
psycopg2: None
jinja2: 2.8
s3fs: 0.1.2
fastparquet: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
@innominate227 Thanks for the report! That indeed looks like a bug (I at least cannot think of a reason why we would do it like this)

I'd like to work on it if nobody minds. May I?
Go ahead! If you have any question, feel free to ask here, or on the gitter channel (https://gitter.im/pydata/pandas) 
(note: it's a bit hard to predict without looking into details at it, but it might not be the most easy issue)

Resolving patch:
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -2107,10 +2107,9 @@ def is_nested_tuple(tup, labels):
     if not isinstance(tup, tuple):
         return False
 
-    # are we nested tuple of: tuple,list,slice
     for i, k in enumerate(tup):
 
-        if isinstance(k, (tuple, list, slice)):
+        if is_list_like(k) or isinstance(k, slice):
             return isinstance(labels, MultiIndex)
 
     return False


Test patch:
diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py
--- a/pandas/tests/indexing/test_loc.py
+++ b/pandas/tests/indexing/test_loc.py
@@ -8,7 +8,7 @@
 
 import pandas as pd
 from pandas.compat import lrange, StringIO
-from pandas import Series, DataFrame, Timestamp, date_range, MultiIndex
+from pandas import Series, DataFrame, Timestamp, date_range, MultiIndex, Index
 from pandas.util import testing as tm
 from pandas.tests.indexing.common import Base
 
@@ -711,3 +711,44 @@ def test_identity_slice_returns_new_object(self):
 
         original_series[:3] = [7, 8, 9]
         assert all(sliced_series[:3] == [7, 8, 9])
+
+    @pytest.mark.parametrize(
+        'indexer_type_1',
+        (list, tuple, set, slice, np.ndarray, Series, Index))
+    @pytest.mark.parametrize(
+        'indexer_type_2',
+        (list, tuple, set, slice, np.ndarray, Series, Index))
+    def test_loc_getitem_nested_indexer(self, indexer_type_1, indexer_type_2):
+        # GH #19686
+        # .loc should work with nested indexers which can be
+        # any list-like objects (see `pandas.api.types.is_list_like`) or slices
+
+        def convert_nested_indexer(indexer_type, keys):
+            if indexer_type == np.ndarray:
+                return np.array(keys)
+            if indexer_type == slice:
+                return slice(*keys)
+            return indexer_type(keys)
+
+        a = [10, 20, 30]
+        b = [1, 2, 3]
+        index = pd.MultiIndex.from_product([a, b])
+        df = pd.DataFrame(
+            np.arange(len(index), dtype='int64'),
+            index=index, columns=['Data'])
+
+        keys = ([10, 20], [2, 3])
+        types = (indexer_type_1, indexer_type_2)
+
+        # check indexers with all the combinations of nested objects
+        # of all the valid types
+        indexer = tuple(
+            convert_nested_indexer(indexer_type, k)
+            for indexer_type, k in zip(types, keys))
+
+        result = df.loc[indexer, 'Data']
+        expected = pd.Series(
+            [1, 2, 4, 5], name='Data',
+            index=pd.MultiIndex.from_product(keys))
+
+        tm.assert_series_equal(result, expected)