Repository: pandas-dev/pandas

Issue:
Stacking with MultiIndex column DataFrame throws an error
Stacking this particular MultiIndex column DataFrame throws an error:

```
import pandas as pd
from StringIO import StringIO

csv = StringIO("""ID,NAME,DATE,VAR1
1,a,03-JAN-2013,69
1,a,04-JAN-2013,77
1,a,05-JAN-2013,75
2,b,03-JAN-2013,69
2,b,04-JAN-2013,75
2,b,05-JAN-2013,72""")

df = pd.read_csv(csv, index_col=['DATE', 'ID'], parse_dates=['DATE'])
df.columns.name = 'Params'

In [11]: df.unstack('ID').resample('W-THU')
Out[11]: 
Params      VAR1      
ID             1     2
DATE                  
2013-01-03    69  69.0
2013-01-10    76  73.5
```

It looks like you ought to be able to stack this over `'ID'`, but it throws an error:

```
In [12]: df.unstack('ID').resample('W-THU').stack('ID')
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-12-e61b475b7bed> in <module>()
----> 1 df.unstack('ID').resample('W-THU').stack('ID')

/Library/Python/2.7/site-packages/pandas/core/frame.pyc in stack(self, level, dropna)
   3950             return result
   3951         else:
-> 3952             return stack(self, level, dropna=dropna)
   3953 
   3954     def unstack(self, level=-1):

/Library/Python/2.7/site-packages/pandas/core/reshape.pyc in stack(frame, level, dropna)
    443 
    444     if isinstance(frame.columns, MultiIndex):
--> 445         return _stack_multi_columns(frame, level=level, dropna=True)
    446     elif isinstance(frame.index, MultiIndex):
    447         new_levels = list(frame.index.levels)

/Library/Python/2.7/site-packages/pandas/core/reshape.pyc in _stack_multi_columns(frame, level, dropna)
    504         # can make more efficient?
    505         if loc.stop - loc.start != levsize:
--> 506             chunk = this.ix[:, this.columns[loc]]
    507             chunk.columns = level_vals.take(chunk.columns.labels[-1])
    508             value_slice = chunk.reindex(columns=level_vals).values

/Library/Python/2.7/site-packages/pandas/core/indexing.pyc in __getitem__(self, key)
     32                 pass
     33 
---> 34             return self._getitem_tuple(key)
     35         else:
     36             return self._getitem_axis(key, axis=0)

/Library/Python/2.7/site-packages/pandas/core/indexing.pyc in _getitem_tuple(self, tup)
    222                 continue
    223 
--> 224             retval = retval.ix._getitem_axis(key, axis=i)
    225 
    226         return retval

/Library/Python/2.7/site-packages/pandas/core/indexing.pyc in _getitem_axis(self, key, axis)
    340                 raise ValueError('Cannot index with multidimensional key')
    341 
--> 342             return self._getitem_iterable(key, axis=axis)
    343         elif axis == 0:
    344             is_int_index = _is_integer_index(labels)

/Library/Python/2.7/site-packages/pandas/core/indexing.pyc in _getitem_iterable(self, key, axis)
    406             # this is not the most robust, but...
    407             if (isinstance(labels, MultiIndex) and
--> 408                     not isinstance(keyarr[0], tuple)):
    409                 level = 0
    410             else:

/Library/Python/2.7/site-packages/pandas/core/index.pyc in __getitem__(self, key)
   1745             retval = []
   1746             for lev, lab in zip(self.levels, self.labels):
-> 1747                 if lab[key] == -1:
   1748                     retval.append(np.nan)
   1749                 else:

IndexError: index out of bounds
```

_Taken from this [SO question](http://stackoverflow.com/questions/15617496/resampling-a-multi-index-dataframe)._



Discussion:
FYI This has been answered on SO:

"The example unstacks a non-numerical column 'NAME' which is silently dropped but causes problems during re-stacking."

```
print df[['VAR1']].unstack('ID').resample('W-THU').stack('ID')
Params         VAR1
DATE       ID
2013-01-03 A   69.0
           B   69.0
2013-01-10 A   76.0
           B   73.5
```

Still seems strange that this is needed (?)

Fixed in master

```
In [12]: df
Out[12]: 
Params        NAME  VAR1
DATE       ID           
2013-01-03 1     a    69
2013-01-04 1     a    77
2013-01-05 1     a    75
2013-01-03 2     b    69
2013-01-04 2     b    75
2013-01-05 2     b    72

In [13]: df.unstack('ID').resample('W-THU')
Out[13]: 
Params      VAR1      
ID             1     2
DATE                  
2013-01-03    69  69.0
2013-01-10    76  73.5
```

@changhiskhan I think this is still visible in master:

```
df.unstack('ID').resample('W-THU').stack('ID')
...
IndexError: index out of bounds
```

?


Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -715,13 +715,16 @@ def diff(self, other):
             raise Exception('Input must be iterable!')
 
         if self.equals(other):
-            return Index([])
+            return Index([], name=self.name)
 
         if not isinstance(other, Index):
             other = np.asarray(other)
+            result_name = self.name
+        else:
+            result_name = self.name if self.name == other.name else None
 
         theDiff = sorted(set(self) - set(other))
-        return Index(theDiff)
+        return Index(theDiff, name=result_name)
 
     def unique(self):
         """
@@ -2508,7 +2511,16 @@ def diff(self, other):
         """
         self._assert_can_do_setop(other)
 
-        result_names = self.names if self.names == other.names else None
+        if not isinstance(other, MultiIndex):
+            if len(other) == 0:
+                return self
+            try:
+                other = MultiIndex.from_tuples(other)
+            except:
+                raise TypeError("other should be a MultiIndex or a list of tuples")
+            result_names = self.names
+        else:
+            result_names = self.names if self.names == other.names else None
 
         if self.equals(other):
             return MultiIndex(levels=[[]] * self.nlevels,
diff --git a/pandas/core/reshape.py b/pandas/core/reshape.py
--- a/pandas/core/reshape.py
+++ b/pandas/core/reshape.py
@@ -17,7 +17,7 @@
 import pandas.algos as algos
 
 
-from pandas.core.index import MultiIndex
+from pandas.core.index import MultiIndex, Index
 
 
 class ReshapeError(Exception):
@@ -159,7 +159,7 @@ def get_new_values(self):
             dtype, fill_value = _maybe_promote(values.dtype)
             new_values = np.empty(result_shape, dtype=dtype)
             new_values.fill(fill_value)
- 
+
         new_mask = np.zeros(result_shape, dtype=bool)
 
         # is there a simpler / faster way of doing this?
@@ -506,11 +506,16 @@ def _stack_multi_columns(frame, level=-1, dropna=True):
     new_data = {}
     level_vals = this.columns.levels[-1]
     levsize = len(level_vals)
+    drop_cols = []
     for key in unique_groups:
         loc = this.columns.get_loc(key)
-
+        slice_len = loc.stop - loc.start
         # can make more efficient?
-        if loc.stop - loc.start != levsize:
+
+        if slice_len == 0:
+            drop_cols.append(key)
+            continue
+        elif slice_len != levsize:
             chunk = this.ix[:, this.columns[loc]]
             chunk.columns = level_vals.take(chunk.columns.labels[-1])
             value_slice = chunk.reindex(columns=level_vals).values
@@ -522,6 +527,9 @@ def _stack_multi_columns(frame, level=-1, dropna=True):
 
         new_data[key] = value_slice.ravel()
 
+    if len(drop_cols) > 0:
+        new_columns = new_columns - drop_cols
+
     N = len(this)
 
     if isinstance(this.index, MultiIndex):


Test patch:
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
@@ -299,12 +299,27 @@ def test_diff(self):
         first = self.strIndex[5:20]
         second = self.strIndex[:10]
         answer = self.strIndex[10:20]
+        first.name = 'name'
+        # different names
         result = first - second
 
         self.assert_(tm.equalContents(result, answer))
+        self.assertEqual(result.name, None)
 
-        diff = first.diff(first)
-        self.assert_(len(diff) == 0)
+        # same names
+        second.name = 'name'
+        result = first - second
+        self.assertEqual(result.name, 'name')
+
+        # with empty
+        result = first.diff([])
+        self.assert_(tm.equalContents(result, first))
+        self.assertEqual(result.name, first.name)
+
+        # with everythin
+        result = first.diff(first)
+        self.assert_(len(result) == 0)
+        self.assertEqual(result.name, first.name)
 
         # non-iterable input
         self.assertRaises(Exception, first.diff, 0.5)
@@ -1511,6 +1526,18 @@ def test_diff(self):
         result = first.diff(first._tuple_index)
         self.assertTrue(result.equals(first[:0]))
 
+        # name from empty array
+        result = first.diff([])
+        self.assert_(first.equals(result))
+        self.assertEqual(first.names, result.names)
+
+        # name from non-empty array 
+        result = first.diff([('foo', 'one')])
+        expected = pd.MultiIndex.from_tuples([('bar', 'one'), ('baz', 'two'), ('foo', 'two'),
+                                              ('qux', 'one'), ('qux', 'two')])
+        expected.names = first.names
+        self.assertEqual(first.names, result.names)
+
     def test_from_tuples(self):
         self.assertRaises(Exception, MultiIndex.from_tuples, [])
 
diff --git a/pandas/tests/test_multilevel.py b/pandas/tests/test_multilevel.py
--- a/pandas/tests/test_multilevel.py
+++ b/pandas/tests/test_multilevel.py
@@ -930,6 +930,24 @@ def test_stack_unstack_multiple(self):
         expected = self.ymd.unstack(2).unstack(1).dropna(axis=1, how='all')
         assert_frame_equal(unstacked, expected.ix[:, unstacked.columns])
 
+    def test_stack_multiple_bug(self):
+        """ bug when some uniques are not present in the data #3170"""
+        id_col = ([1] * 3) + ([2] * 3)
+        name = (['a'] * 3) + (['b'] * 3)
+        date = pd.to_datetime(['2013-01-03', '2013-01-04', '2013-01-05'] * 2)
+        var1 = np.random.randint(0, 100, 6)
+        df = DataFrame(dict(ID=id_col, NAME=name, DATE=date, VAR1=var1))
+
+        multi = df.set_index(['DATE', 'ID'])
+        multi.columns.name = 'Params'
+        unst = multi.unstack('ID')
+        down = unst.resample('W-THU')
+
+        rs = down.stack('ID')
+        xp = unst.ix[:, ['VAR1']].resample('W-THU').stack('ID')
+        xp.columns.name = 'Params'
+        assert_frame_equal(rs, xp)
+
     def test_unstack_multiple_hierarchical(self):
         df = DataFrame(index=[[0, 0, 0, 0, 1, 1, 1, 1],
                               [0, 0, 1, 1, 0, 0, 1, 1],