From ed324e8692002db49e8d587a9bb73d28d8776ffe Mon Sep 17 00:00:00 2001 From: Brandon Rhodes Date: Fri, 22 Apr 2016 13:01:10 -0400 Subject: [PATCH] Add defaults to swaplevel() parameters i and j Closes #12934 feature request. Author: Brandon Rhodes Closes #12943 from brandon-rhodes/master and squashes the following commits: c6fbb3e [Brandon Rhodes] Add default values to swaplevel() i, j parameters --- doc/source/whatsnew/v0.18.1.txt | 4 +++- pandas/core/frame.py | 8 +++++++- pandas/core/generic.py | 8 +++++++- pandas/core/series.py | 8 +++++++- pandas/indexes/multi.py | 8 +++++++- pandas/tests/indexes/test_multi.py | 15 +++++++++------ pandas/tests/test_multilevel.py | 24 +++++++++++++++++------- 7 files changed, 57 insertions(+), 18 deletions(-) diff --git a/doc/source/whatsnew/v0.18.1.txt b/doc/source/whatsnew/v0.18.1.txt index 8d45be77ecb65..9cec350c5087c 100644 --- a/doc/source/whatsnew/v0.18.1.txt +++ b/doc/source/whatsnew/v0.18.1.txt @@ -4,7 +4,7 @@ v0.18.1 (April ??, 2016) ------------------------ This is a minor bug-fix release from 0.18.0 and includes a large number of -bug fixes along several new features, enhancements, and performance improvements. +bug fixes along with several new features, enhancements, and performance improvements. We recommend that all users upgrade to this version. Highlights include: @@ -131,6 +131,8 @@ These changes conform sparse handling to return the correct types and work to ma API changes ~~~~~~~~~~~ +- ``.swaplevel()`` for ``Series``, ``DataFrame``, ``Panel``, and ``MultiIndex`` now features defaults for its first two parameters ``i`` and ``j`` that swap the two innermost levels of the index. (:issue:`12934`) + - ``.searchsorted()`` for ``Index`` and ``TimedeltaIndex`` now accept a ``sorter`` argument to maintain compatibility with numpy's ``searchsorted`` function (:issue:`12238`) - ``Period`` and ``PeriodIndex`` now raises ``IncompatibleFrequency`` error which inherits ``ValueError`` rather than raw ``ValueError`` (:issue:`12615`) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index c598a2b719f82..55ec247936482 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3384,7 +3384,7 @@ def nsmallest(self, n, columns, keep='first'): """ return self._nsorted(columns, n, 'nsmallest', keep) - def swaplevel(self, i, j, axis=0): + def swaplevel(self, i=-2, j=-1, axis=0): """ Swap levels i and j in a MultiIndex on a particular axis @@ -3396,6 +3396,12 @@ def swaplevel(self, i, j, axis=0): Returns ------- swapped : type of caller (new object) + + .. versionchanged:: 0.18.1 + + The indexes ``i`` and ``j`` are now optional, and default to + the two innermost levels of the index. + """ result = self.copy() diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 30252f7068424..3b68bfc6b05ea 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -522,7 +522,7 @@ def squeeze(self): except: return self - def swaplevel(self, i, j, axis=0): + def swaplevel(self, i=-2, j=-1, axis=0): """ Swap levels i and j in a MultiIndex on a particular axis @@ -534,6 +534,12 @@ def swaplevel(self, i, j, axis=0): Returns ------- swapped : type of caller (new object) + + .. versionchanged:: 0.18.1 + + The indexes ``i`` and ``j`` are now optional, and default to + the two innermost levels of the index. + """ axis = self._get_axis_number(axis) result = self.copy() diff --git a/pandas/core/series.py b/pandas/core/series.py index 9fc1bc0dbe969..e172ef7db58e6 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1949,7 +1949,7 @@ def sortlevel(self, level=0, ascending=True, sort_remaining=True): return self.sort_index(level=level, ascending=ascending, sort_remaining=sort_remaining) - def swaplevel(self, i, j, copy=True): + def swaplevel(self, i=-2, j=-1, copy=True): """ Swap levels i and j in a MultiIndex @@ -1961,6 +1961,12 @@ def swaplevel(self, i, j, copy=True): Returns ------- swapped : Series + + .. versionchanged:: 0.18.1 + + The indexes ``i`` and ``j`` are now optional, and default to + the two innermost levels of the index. + """ new_index = self.index.swaplevel(i, j) return self._constructor(self._values, index=new_index, diff --git a/pandas/indexes/multi.py b/pandas/indexes/multi.py index 15fa93ebc5af3..a4d8ea059dd7d 100644 --- a/pandas/indexes/multi.py +++ b/pandas/indexes/multi.py @@ -1194,7 +1194,7 @@ def droplevel(self, level=0): return MultiIndex(levels=new_levels, labels=new_labels, names=new_names, verify_integrity=False) - def swaplevel(self, i, j): + def swaplevel(self, i=-2, j=-1): """ Swap level i with level j. Do not change the ordering of anything @@ -1206,6 +1206,12 @@ def swaplevel(self, i, j): Returns ------- swapped : MultiIndex + + .. versionchanged:: 0.18.1 + + The indexes ``i`` and ``j`` are now optional, and default to + the two innermost levels of the index. + """ new_levels = list(self.levels) new_labels = list(self.labels) diff --git a/pandas/tests/indexes/test_multi.py b/pandas/tests/indexes/test_multi.py index d956d38b4cad4..c585fb1b1b21f 100644 --- a/pandas/tests/indexes/test_multi.py +++ b/pandas/tests/indexes/test_multi.py @@ -2095,12 +2095,15 @@ def test_partial_string_timestamp_multiindex(self): # c 14 # partial string matching on a single index - df_swap = df.swaplevel(0, 1).sort_index() - just_a = df_swap.loc['a'] - result = just_a.loc['2016-01-01'] - expected = df.loc[idx[:, 'a'], :].iloc[0:2] - expected.index = expected.index.droplevel(1) - tm.assert_frame_equal(result, expected) + for df_swap in (df.swaplevel(), + df.swaplevel(0), + df.swaplevel(0, 1)): + df_swap = df_swap.sort_index() + just_a = df_swap.loc['a'] + result = just_a.loc['2016-01-01'] + expected = df.loc[idx[:, 'a'], :].iloc[0:2] + expected.index = expected.index.droplevel(1) + tm.assert_frame_equal(result, expected) # indexing with IndexSlice result = df.loc[idx['2016-01-01':'2016-02-01', :], :] diff --git a/pandas/tests/test_multilevel.py b/pandas/tests/test_multilevel.py index 2195192f70778..63a8b49ab4b00 100644 --- a/pandas/tests/test_multilevel.py +++ b/pandas/tests/test_multilevel.py @@ -1320,15 +1320,23 @@ def test_join(self): ) # TODO what should join do with names ? def test_swaplevel(self): - swapped = self.frame['A'].swaplevel(0, 1) - swapped2 = self.frame['A'].swaplevel('first', 'second') + swapped = self.frame['A'].swaplevel() + swapped2 = self.frame['A'].swaplevel(0) + swapped3 = self.frame['A'].swaplevel(0, 1) + swapped4 = self.frame['A'].swaplevel('first', 'second') self.assertFalse(swapped.index.equals(self.frame.index)) assert_series_equal(swapped, swapped2) + assert_series_equal(swapped, swapped3) + assert_series_equal(swapped, swapped4) - back = swapped.swaplevel(0, 1) - back2 = swapped.swaplevel('second', 'first') + back = swapped.swaplevel() + back2 = swapped.swaplevel(0) + back3 = swapped.swaplevel(0, 1) + back4 = swapped.swaplevel('second', 'first') self.assertTrue(back.index.equals(self.frame.index)) assert_series_equal(back, back2) + assert_series_equal(back, back3) + assert_series_equal(back, back4) ft = self.frame.T swapped = ft.swaplevel('first', 'second', axis=1) @@ -1337,11 +1345,13 @@ def test_swaplevel(self): def test_swaplevel_panel(self): panel = Panel({'ItemA': self.frame, 'ItemB': self.frame * 2}) - - result = panel.swaplevel(0, 1, axis='major') expected = panel.copy() expected.major_axis = expected.major_axis.swaplevel(0, 1) - tm.assert_panel_equal(result, expected) + + for result in (panel.swaplevel(axis='major'), + panel.swaplevel(0, axis='major'), + panel.swaplevel(0, 1, axis='major')): + tm.assert_panel_equal(result, expected) def test_reorder_levels(self): result = self.ymd.reorder_levels(['month', 'day', 'year'])