Repository: pandas-dev/pandas

Issue:
DataFrame.stack() raises TypeError on mixed type level under Python 3
#### Code Sample, a copy-pastable example if possible

```python
In [2]: df = pd.DataFrame(1, index=range(3), columns=pd.MultiIndex.from_product([['a', 'b'], [1, 2]]))

In [3]: df['c'] = 2

In [4]: df.stack(0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-a437419299b6> in <module>()
----> 1 df.stack(0)

/home/nobackup/repo/pandas/pandas/core/frame.py in stack(self, level, dropna)
   4500             return stack_multiple(self, level, dropna=dropna)
   4501         else:
-> 4502             return stack(self, level, dropna=dropna)
   4503 
   4504     def unstack(self, level=-1, fill_value=None):

/home/nobackup/repo/pandas/pandas/core/reshape/reshape.py in stack(frame, level, dropna)
    513 
    514     if isinstance(frame.columns, MultiIndex):
--> 515         return _stack_multi_columns(frame, level_num=level_num, dropna=dropna)
    516     elif isinstance(frame.index, MultiIndex):
    517         new_levels = list(frame.index.levels)

/home/nobackup/repo/pandas/pandas/core/reshape/reshape.py in _stack_multi_columns(frame, level_num, dropna)
    619         # level
    620         level_to_sort = _convert_level_number(0, this.columns)
--> 621         this = this.sort_index(level=level_to_sort, axis=1)
    622 
    623     # tuple list excluding level for grouping columns

/home/nobackup/repo/pandas/pandas/core/frame.py in sort_index(self, axis, level, ascending, inplace, kind, na_position, sort_remaining, by)
   3673             # make sure that the axis is lexsorted to start
   3674             # if not we need to reconstruct to get the correct indexer
-> 3675             labels = labels._sort_levels_monotonic()
   3676             indexer = lexsort_indexer(labels._get_labels_for_sorting(),
   3677                                       orders=ascending,

/home/nobackup/repo/pandas/pandas/core/indexes/multi.py in _sort_levels_monotonic(self)
   1275 
   1276             # indexer to reorder the levels
-> 1277             indexer = lev.argsort()
   1278             lev = lev.take(indexer)
   1279 

/home/nobackup/repo/pandas/pandas/core/indexes/base.py in argsort(self, *args, **kwargs)
   2146         if result is None:
   2147             result = np.array(self)
-> 2148         return result.argsort(*args, **kwargs)
   2149 
   2150     def __add__(self, other):

TypeError: unorderable types: str() > int()

```

#### Problem description

This is even more confusing because ``df[['a', 'b']].stack(0)`` (where no mixed dtype is left) results in the same error.

#### Expected Output

```python
In [13]: df.stack(0)
Out[13]: 
       1    2    
0 a  1.0  1.0  NaN
  b  1.0  1.0  NaN
  c  NaN  NaN  2.0
1 a  1.0  1.0  NaN
  b  1.0  1.0  NaN
  c  NaN  NaN  2.0
2 a  1.0  1.0  NaN
  b  1.0  1.0  NaN
  c  NaN  NaN  2.0


```

#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]

</details>



Discussion:
Very odd indeed.  Looking at the stacktrace, what is the value of `result` when the code breaks?  Probably a good starting point to find the bug and patch.

Resolving patch:
diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py
--- a/pandas/core/indexes/multi.py
+++ b/pandas/core/indexes/multi.py
@@ -1292,19 +1292,19 @@ def _sort_levels_monotonic(self):
 
         for lev, lab in zip(self.levels, self.labels):
 
-            if lev.is_monotonic:
-                new_levels.append(lev)
-                new_labels.append(lab)
-                continue
-
-            # indexer to reorder the levels
-            indexer = lev.argsort()
-            lev = lev.take(indexer)
+            if not lev.is_monotonic:
+                try:
+                    # indexer to reorder the levels
+                    indexer = lev.argsort()
+                except TypeError:
+                    pass
+                else:
+                    lev = lev.take(indexer)
 
-            # indexer to reorder the labels
-            indexer = _ensure_int64(indexer)
-            ri = lib.get_reverse_indexer(indexer, len(indexer))
-            lab = algos.take_1d(ri, lab)
+                    # indexer to reorder the labels
+                    indexer = _ensure_int64(indexer)
+                    ri = lib.get_reverse_indexer(indexer, len(indexer))
+                    lab = algos.take_1d(ri, lab)
 
             new_levels.append(lev)
             new_labels.append(lab)


Test patch:
diff --git a/pandas/tests/frame/test_reshape.py b/pandas/tests/frame/test_reshape.py
--- a/pandas/tests/frame/test_reshape.py
+++ b/pandas/tests/frame/test_reshape.py
@@ -133,6 +133,30 @@ def test_stack_unstack(self):
         assert_frame_equal(unstacked_cols.T, df)
         assert_frame_equal(unstacked_cols_df['bar'].T, df)
 
+    def test_stack_mixed_level(self):
+        # GH 18310
+        levels = [range(3), [3, 'a', 'b'], [1, 2]]
+
+        # flat columns:
+        df = DataFrame(1, index=levels[0], columns=levels[1])
+        result = df.stack()
+        expected = Series(1, index=MultiIndex.from_product(levels[:2]))
+        assert_series_equal(result, expected)
+
+        # MultiIndex columns:
+        df = DataFrame(1, index=levels[0],
+                       columns=MultiIndex.from_product(levels[1:]))
+        result = df.stack(1)
+        expected = DataFrame(1, index=MultiIndex.from_product([levels[0],
+                                                               levels[2]]),
+                             columns=levels[1])
+        assert_frame_equal(result, expected)
+
+        # as above, but used labels in level are actually of homogeneous type
+        result = df[['a', 'b']].stack(1)
+        expected = expected[['a', 'b']]
+        assert_frame_equal(result, expected)
+
     def test_unstack_fill(self):
 
         # GH #9746: fill_value keyword argument for Series