Repository: pandas-dev/pandas

Issue:
BUG: Panel.iloc[] bug with MultiIndex axis (another one)
I'd expect the [7] to produce the same result as [6], but instead it gives an error.

```
Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AMD64)]
Type "copyright", "credits" or "license" for more information.

IPython 2.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from numpy import zeros

In [2]: from pandas import Panel, MultiIndex

In [3]: p1 = Panel(zeros((3,3,3)), items=['a','b','c'], major_axis=['x','y','z'], minor_axis=['u','v','w'])

In [4]: mi = MultiIndex.from_tuples([(0,'x'), (1,'y'), (1,'z')])

In [5]: p2 = Panel(zeros((3,3,3)), items=['a','b','c'], major_axis=mi, minor_axis=['u','v','w'])

In [6]: p1.iloc[:, 1, 0]
Out[6]:
a    0
b    0
c    0
Name: u, dtype: float64

In [7]: p2.iloc[:, 1, 0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-f7c04712ad2b> in <module>()
----> 1 p2.iloc[:, 1, 0]

C:\Python34\lib\site-packages\pandas\core\indexing.py in __getitem__(self, key)
   1125     def __getitem__(self, key):
   1126         if type(key) is tuple:
-> 1127             return self._getitem_tuple(key)
   1128         else:
   1129             return self._getitem_axis(key, axis=0)

C:\Python34\lib\site-packages\pandas\core\indexing.py in _getitem_tuple(self, tup)
   1332                 continue
   1333
-> 1334             retval = getattr(retval, self.name)._getitem_axis(key, axis=axis)
   1335
   1336             # if the dim was reduced, then pass a lower-dim the next time

C:\Python34\lib\site-packages\pandas\core\indexing.py in _getitem_axis(self, key, axis, validate_iterable)
   1386                 self._is_valid_integer(key, axis)
   1387
-> 1388             return self._get_loc(key, axis=axis)
   1389
   1390     def _convert_to_indexer(self, obj, axis=0, is_setter=False):

C:\Python34\lib\site-packages\pandas\core\indexing.py in _get_loc(self, key, axis)
     87
     88     def _get_loc(self, key, axis=0):
---> 89         return self.obj._ixs(key, axis=axis)
     90
     91     def _slice(self, obj, axis=0, typ=None):

C:\Python34\lib\site-packages\pandas\core\panel.py in _ixs(self, i, axis)
    803         if _is_list_like(key):
    804             indexer = {self._get_axis_name(axis): key}
--> 805             return self.reindex(**indexer)
    806
    807         # a reduction

C:\Python34\lib\site-packages\pandas\core\panel.py in reindex(self, items, major_axis, minor_axis, **kwargs)
   1109                       else kwargs.pop('minor', None))
   1110         return super(Panel, self).reindex(items=items, major_axis=major_axis,
-> 1111                                           minor_axis=minor_axis, **kwargs)
   1112
   1113     @Appender(_shared_docs['rename'] % _shared_doc_kwargs)

C:\Python34\lib\site-packages\pandas\core\generic.py in reindex(self, *args, **kwargs)
   1622         # perform the reindex on the axes
   1623         return self._reindex_axes(axes, level, limit,
-> 1624                                   method, fill_value, copy).__finalize__(self)
   1625
   1626     def _reindex_axes(self, axes, level, limit, method, fill_value, copy):

C:\Python34\lib\site-packages\pandas\core\generic.py in _reindex_axes(self, axes, level, limit, method, fill_value, copy)
   1639             ax = self._get_axis(a)
   1640             new_index, indexer = ax.reindex(
-> 1641                 labels, level=level, limit=limit, method=method)
   1642
   1643             obj = obj._reindex_with_indexers(

C:\Python34\lib\site-packages\pandas\core\index.py in reindex(self, target, method, level, limit, copy_if_needed)
   3256             else:
   3257                 # hopefully?
-> 3258                 target = MultiIndex.from_tuples(target)
   3259
   3260         return target, indexer

C:\Python34\lib\site-packages\pandas\core\index.py in from_tuples(cls, tuples, sortorder, names)
   2809                 tuples = tuples.values
   2810
-> 2811             arrays = list(lib.tuples_to_object_array(tuples).T)
   2812         elif isinstance(tuples, list):
   2813             arrays = list(lib.to_object_array_tuples(tuples).T)

C:\Python34\lib\site-packages\pandas\lib.pyd in pandas.lib.tuples_to_object_array (pandas\lib.c:52859)()

TypeError: object of type 'numpy.int64' has no len()
```



Discussion:

Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -1632,11 +1632,11 @@ def _reindex_axes(self, axes, level, limit, method, fill_value, copy):
                 continue
 
             # convert to an index if we are not a multi-selection
+            ax = self._get_axis(a)
             if level is None:
                 labels = _ensure_index(labels)
 
             axis = self._get_axis_number(a)
-            ax = self._get_axis(a)
             new_index, indexer = ax.reindex(
                 labels, level=level, limit=limit, method=method)
 
@@ -1929,11 +1929,11 @@ def _get_bool_data(self):
 
     def as_matrix(self, columns=None):
         """
-        Convert the frame to its Numpy-array representation. 
-                
+        Convert the frame to its Numpy-array representation.
+
         Parameters
         ----------
-        columns: list, optional, default:None 
+        columns: list, optional, default:None
             If None, return all columns, otherwise, returns specified columns.
 
         Returns
@@ -1942,23 +1942,23 @@ def as_matrix(self, columns=None):
             If the caller is heterogeneous and contains booleans or objects,
             the result will be of dtype=object. See Notes.
 
-            
+
         Notes
-        ----- 
+        -----
         Return is NOT a Numpy-matrix, rather, a Numpy-array.
-        
+
         The dtype will be a lower-common-denominator dtype (implicit
         upcasting); that is to say if the dtypes (even of numeric types)
         are mixed, the one that accommodates all will be chosen. Use this
         with care if you are not dealing with the blocks.
 
-        e.g. If the dtypes are float16 and float32, dtype will be upcast to 
-        float32.  If dtypes are int32 and uint8, dtype will be upcase to 
+        e.g. If the dtypes are float16 and float32, dtype will be upcast to
+        float32.  If dtypes are int32 and uint8, dtype will be upcase to
         int32.
 
         This method is provided for backwards compatibility. Generally,
         it is recommended to use '.values'.
-                            
+
         See Also
         --------
         pandas.DataFrame.values
@@ -1971,7 +1971,7 @@ def as_matrix(self, columns=None):
     @property
     def values(self):
         """Numpy representation of NDFrame
-        
+
         Notes
         -----
         The dtype will be a lower-common-denominator dtype (implicit
@@ -1979,8 +1979,8 @@ def values(self):
         are mixed, the one that accommodates all will be chosen. Use this
         with care if you are not dealing with the blocks.
 
-        e.g. If the dtypes are float16 and float32, dtype will be upcast to 
-        float32.  If dtypes are int32 and uint8, dtype will be upcase to 
+        e.g. If the dtypes are float16 and float32, dtype will be upcast to
+        float32.  If dtypes are int32 and uint8, dtype will be upcase to
         int32.
         """
         return self.as_matrix()
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -816,7 +816,7 @@ def _getitem_nested_tuple(self, tup):
         # this is iterative
         obj = self.obj
         axis = 0
-        for key in tup:
+        for i, key in enumerate(tup):
 
             if _is_null_slice(key):
                 axis += 1
@@ -833,6 +833,13 @@ def _getitem_nested_tuple(self, tup):
             # has the dim of the obj changed?
             # GH 7199
             if obj.ndim < current_ndim:
+
+                # GH 7516
+                # if had a 3 dim and are going to a 2d
+                # axes are reversed on a DataFrame
+                if i >= 1 and current_ndim == 3 and obj.ndim == 2:
+                    obj = obj.T
+
                 axis -= 1
 
         return obj
diff --git a/pandas/core/panel.py b/pandas/core/panel.py
--- a/pandas/core/panel.py
+++ b/pandas/core/panel.py
@@ -797,12 +797,15 @@ def _ixs(self, i, axis=0):
         axis : int
         """
 
-        key = self._get_axis(axis)[i]
+        ax = self._get_axis(axis)
+        key = ax[i]
 
         # xs cannot handle a non-scalar key, so just reindex here
-        if _is_list_like(key):
-            indexer = {self._get_axis_name(axis): key}
-            return self.reindex(**indexer)
+        # if we have a multi-index and a single tuple, then its a reduction (GH 7516)
+        if not (isinstance(ax, MultiIndex) and isinstance(key, tuple)):
+            if _is_list_like(key):
+                indexer = {self._get_axis_name(axis): key}
+                return self.reindex(**indexer)
 
         # a reduction
         if axis == 0:


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -1179,6 +1179,16 @@ def f():
         result2 = wd2.iloc[0,[0],[0,1,2]]
         assert_frame_equal(result2,expected2)
 
+        # GH 7516
+        mi = MultiIndex.from_tuples([(0,'x'), (1,'y'), (2,'z')])
+        p = Panel(np.arange(3*3*3,dtype='int64').reshape(3,3,3), items=['a','b','c'], major_axis=mi, minor_axis=['u','v','w'])
+        result = p.iloc[:, 1, 0]
+        expected = Series([3,12,21],index=['a','b','c'], name='u')
+        assert_series_equal(result,expected)
+
+        result = p.loc[:, (1,'y'), 'u']
+        assert_series_equal(result,expected)
+
     def test_iloc_getitem_doc_issue(self):
 
         # multi axis slicing issue with single block