Repository: pandas-dev/pandas

Issue:
BUG: Panel.iloc[] bug with MultiIndex axis
I just posted this at https://groups.google.com/forum/#!topic/pydata/wmUjOttpcpI.

All of [6], [7], [8], and [9] below should have the same dimensions, but [8] is incorrect. Similarly, [12] should work and look like [14]. The only difference between wd1 and wd2 is that the former has minor_axis=multi_index, whereas the latter has minor_axis=simple_index.

```
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.0.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]: import pandas as pd

In [2]: multi_index = pd.MultiIndex.from_tuples([('ONE', 'one'),
                                                 ('TWO', 'two'),
                                                 ('THREE', 'three')],
                                                names=['UPPER', 'lower'])

In [3]: simple_index = [x[0] for x in multi_index]

In [4]: wd1 = pd.Panel(items=['First', 'Second'],
                       major_axis=['a', 'b', 'c', 'd'],
                       minor_axis=multi_index)

In [5]: wd2 = pd.Panel(items=['First', 'Second'],
                       major_axis=['a', 'b', 'c', 'd'],
                       minor_axis=simple_index)

In [6]: wd1['First'].iloc[[True, True, True, False], [0, 2]]
Out[6]:
UPPER  ONE  THREE
lower  one  three
a      NaN    NaN
b      NaN    NaN
c      NaN    NaN

In [7]: wd2['First'].iloc[[True, True, True, False], [0, 2]]
Out[7]:
   ONE  THREE
a  NaN    NaN
b  NaN    NaN
c  NaN    NaN

In [8]: wd1.iloc[0, [True, True, True, False], [0, 2]]  # WRONG
Out[8]:
UPPER  ONE  TWO  THREE
lower  one  two  three
a      NaN  NaN    NaN
c      NaN  NaN    NaN

In [9]: wd2.iloc[0, [True, True, True, False], [0, 2]]
Out[9]:
   ONE  THREE
a  NaN    NaN
b  NaN    NaN
c  NaN    NaN

In [10]: pd.__version__
Out[10]: '0.14.0rc1-39-g9d01fe1'

In [11]: wd1.iloc[0,0,[0,1,2]]
Out[11]:
UPPER  lower
ONE    one     NaN
TWO    two     NaN
THREE  three   NaN
Name: a, dtype: float64

In [12]: wd1.iloc[0,[0],[0,1,2]] # should be similar to [14] below
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-12-b14471499110> in <module>()
----> 1 wd1.iloc[0,[0],[0,1,2]]

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

C:\Python34\lib\site-packages\pandas\core\indexing.py in _getitem_tuple(self, tup)
   1329                 continue
   1330
-> 1331             retval = getattr(retval, self.name)._getitem_axis(key, axis=axis)
   1332
   1333             # 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)
   1368
   1369                 # validate list bounds
-> 1370                 self._is_valid_list_like(key, axis)
   1371
   1372                 # force an actual list

C:\Python34\lib\site-packages\pandas\core\indexing.py in _is_valid_list_like(self, key, axis)
   1307         l = len(ax)
   1308         if len(arr) and (arr.max() >= l or arr.min() <= -l):
-> 1309             raise IndexError("positional indexers are out-of-bounds")
   1310
   1311         return True

IndexError: positional indexers are out-of-bounds

In [13]: wd2.iloc[0,0,[0,1,2]]
Out[13]:
ONE     NaN
TWO     NaN
THREE   NaN
Name: a, dtype: float64

In [14]: wd2.iloc[0,[0],[0,1,2]]
Out[14]:
   ONE  TWO  THREE
a  NaN  NaN    NaN
```



Discussion:

Resolving patch:
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -821,10 +821,13 @@ def _getitem_nested_tuple(self, tup):
                 axis += 1
                 continue
 
+            current_ndim = obj.ndim
             obj = getattr(obj, self.name)._getitem_axis(key, axis=axis, validate_iterable=True)
             axis += 1
 
-            if obj.ndim < self.ndim:
+            # has the dim of the obj changed?
+            # GH 7199
+            if obj.ndim < current_ndim:
                 axis -= 1
 
         return obj


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
@@ -1099,6 +1099,38 @@ def f():
             p.iloc[0,[True,True,True],[2]]
         self.assertRaises(IndexError, f)
 
+        # GH 7199
+        # Panel with multi-index
+        multi_index = pd.MultiIndex.from_tuples([('ONE', 'one'),
+                                                 ('TWO', 'two'),
+                                                 ('THREE', 'three')],
+                                                names=['UPPER', 'lower'])
+
+        simple_index = [x[0] for x in multi_index]
+        wd1 = Panel(items=['First', 'Second'],
+                    major_axis=['a', 'b', 'c', 'd'],
+                    minor_axis=multi_index)
+
+        wd2 = Panel(items=['First', 'Second'],
+                    major_axis=['a', 'b', 'c', 'd'],
+                    minor_axis=simple_index)
+
+        expected1 = wd1['First'].iloc[[True, True, True, False], [0, 2]]
+        result1 = wd1.iloc[0, [True, True, True, False], [0, 2]]  # WRONG
+        assert_frame_equal(result1,expected1)
+
+        expected2 = wd2['First'].iloc[[True, True, True, False], [0, 2]]
+        result2 = wd2.iloc[0, [True, True, True, False], [0, 2]]
+        assert_frame_equal(result2,expected2)
+
+        expected1 = DataFrame(index=['a'],columns=multi_index,dtype='float64')
+        result1 = wd1.iloc[0,[0],[0,1,2]]
+        assert_frame_equal(result1,expected1)
+
+        expected2 = DataFrame(index=['a'],columns=simple_index,dtype='float64')
+        result2 = wd2.iloc[0,[0],[0,1,2]]
+        assert_frame_equal(result2,expected2)
+
     def test_iloc_getitem_doc_issue(self):
 
         # multi axis slicing issue with single block