diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 9afcf3ddcdbb1..3e00ec9cbb742 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -390,6 +390,7 @@ Groupby/Resample/Rolling - Bug in :meth:`pandas.core.groupby.GroupBy.idxmax` and :meth:`pandas.core.groupby.GroupBy.idxmin` with datetime column would return incorrect dtype (:issue:`25444`, :issue:`15306`) - Bug in :meth:`pandas.core.groupby.GroupBy.cumsum`, :meth:`pandas.core.groupby.GroupBy.cumprod`, :meth:`pandas.core.groupby.GroupBy.cummin` and :meth:`pandas.core.groupby.GroupBy.cummax` with categorical column having absent categories, would return incorrect result or segfault (:issue:`16771`) + Reshaping ^^^^^^^^^ @@ -402,6 +403,7 @@ Reshaping - Bug in :func:`merge` where merging with equivalent Categorical dtypes was raising an error (:issue:`22501`) - Bug in :class:`DataFrame` constructor when passing non-empty tuples would cause a segmentation fault (:issue:`25691`) - Bug in :func:`pandas.cut` where large bins could incorrectly raise an error due to an integer overflow (:issue:`26045`) +- Bug in :func:`DataFrame.sort_index` where an error is thrown when a multi-indexed DataFrame is sorted on all levels with the initial level sorted last (:issue:`26053`) - Bug in :meth:`Series.nlargest` treats ``True`` as smaller than ``False`` (:issue:`26154`) Sparse diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 34413f441a5d6..1510b965ea614 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2084,8 +2084,14 @@ def sortlevel(self, level=0, ascending=True, sort_remaining=True): shape = list(self.levshape) # partition codes and shape - primary = tuple(codes.pop(lev - i) for i, lev in enumerate(level)) - primshp = tuple(shape.pop(lev - i) for i, lev in enumerate(level)) + primary = tuple(codes[lev] for lev in level) + primshp = tuple(shape[lev] for lev in level) + + # Reverse sorted to retain the order of + # smaller indices that needs to be removed + for lev in sorted(level, reverse=True): + codes.pop(lev) + shape.pop(lev) if sort_remaining: primary += primary + tuple(codes) diff --git a/pandas/tests/frame/test_sorting.py b/pandas/tests/frame/test_sorting.py index e7d04319d173e..49e37d9ca24a3 100644 --- a/pandas/tests/frame/test_sorting.py +++ b/pandas/tests/frame/test_sorting.py @@ -496,11 +496,28 @@ def test_sort_index_duplicates(self): def test_sort_index_level(self): mi = MultiIndex.from_tuples([[1, 1, 3], [1, 1, 1]], names=list('ABC')) df = DataFrame([[1, 2], [3, 4]], mi) - res = df.sort_index(level='A', sort_remaining=False) - assert_frame_equal(df, res) - res = df.sort_index(level=['A', 'B'], sort_remaining=False) - assert_frame_equal(df, res) + result = df.sort_index(level='A', sort_remaining=False) + expected = df + assert_frame_equal(result, expected) + + result = df.sort_index(level=['A', 'B'], sort_remaining=False) + expected = df + assert_frame_equal(result, expected) + + # Error thrown by sort_index when + # first index is sorted last (#26053) + result = df.sort_index(level=['C', 'B', 'A']) + expected = df.iloc[[1, 0]] + assert_frame_equal(result, expected) + + result = df.sort_index(level=['B', 'C', 'A']) + expected = df.iloc[[1, 0]] + assert_frame_equal(result, expected) + + result = df.sort_index(level=['C', 'A']) + expected = df.iloc[[1, 0]] + assert_frame_equal(result, expected) def test_sort_index_categorical_index(self):