Skip to content

Commit b9c1786

Browse files
vbardajreback
authored andcommitted
API: Add DataFrame.droplevel (#21871)
1 parent 0d858c4 commit b9c1786

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

doc/source/api.rst

+2
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ Reindexing / Selection / Label manipulation
444444

445445
Series.align
446446
Series.drop
447+
Series.droplevel
447448
Series.drop_duplicates
448449
Series.duplicated
449450
Series.equals
@@ -1063,6 +1064,7 @@ Reshaping, sorting, transposing
10631064
.. autosummary::
10641065
:toctree: generated/
10651066

1067+
DataFrame.droplevel
10661068
DataFrame.pivot
10671069
DataFrame.pivot_table
10681070
DataFrame.reorder_levels

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Other Enhancements
109109
- :func:`Series.mode` and :func:`DataFrame.mode` now support the ``dropna`` parameter which can be used to specify whether NaN/NaT values should be considered (:issue:`17534`)
110110
- :func:`to_csv` now supports ``compression`` keyword when a file handle is passed. (:issue:`21227`)
111111
- :meth:`Index.droplevel` is now implemented also for flat indexes, for compatibility with :class:`MultiIndex` (:issue:`21115`)
112+
- :meth:`Series.droplevel` and :meth:`DataFrame.droplevel` are now implemented (:issue:`20342`)
112113
- Added support for reading from Google Cloud Storage via the ``gcsfs`` library (:issue:`19454`)
113114
- :func:`to_gbq` and :func:`read_gbq` signature and documentation updated to
114115
reflect changes from the `Pandas-GBQ library version 0.5.0

pandas/core/generic.py

+60
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,66 @@ def swapaxes(self, axis1, axis2, copy=True):
716716

717717
return self._constructor(new_values, *new_axes).__finalize__(self)
718718

719+
def droplevel(self, level, axis=0):
720+
"""Return DataFrame with requested index / column level(s) removed.
721+
722+
.. versionadded:: 0.24.0
723+
724+
Parameters
725+
----------
726+
level : int, str, or list-like
727+
If a string is given, must be the name of a level
728+
If list-like, elements must be names or positional indexes
729+
of levels.
730+
731+
axis : {0 or 'index', 1 or 'columns'}, default 0
732+
733+
734+
Returns
735+
-------
736+
DataFrame.droplevel()
737+
738+
Examples
739+
--------
740+
>>> df = pd.DataFrame([
741+
...: [1, 2, 3, 4],
742+
...: [5, 6, 7, 8],
743+
...: [9, 10, 11, 12]
744+
...: ]).set_index([0, 1]).rename_axis(['a', 'b'])
745+
746+
>>> df.columns = pd.MultiIndex.from_tuples([
747+
...: ('c', 'e'), ('d', 'f')
748+
...:], names=['level_1', 'level_2'])
749+
750+
>>> df
751+
level_1 c d
752+
level_2 e f
753+
a b
754+
1 2 3 4
755+
5 6 7 8
756+
9 10 11 12
757+
758+
>>> df.droplevel('a')
759+
level_1 c d
760+
level_2 e f
761+
b
762+
2 3 4
763+
6 7 8
764+
10 11 12
765+
766+
>>> df.droplevel('level2', axis=1)
767+
level_1 c d
768+
a b
769+
1 2 3 4
770+
5 6 7 8
771+
9 10 11 12
772+
773+
"""
774+
labels = self._get_axis(axis)
775+
new_labels = labels.droplevel(level)
776+
result = self.set_axis(new_labels, axis=axis, inplace=False)
777+
return result
778+
719779
def pop(self, item):
720780
"""
721781
Return item and drop from frame. Raise KeyError if not found.

pandas/tests/frame/test_alter_axes.py

+22
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,28 @@ def test_reindex_signature(self):
10561056
"limit", "copy", "level", "method",
10571057
"fill_value", "tolerance"}
10581058

1059+
def test_droplevel(self):
1060+
# GH20342
1061+
df = pd.DataFrame([
1062+
[1, 2, 3, 4],
1063+
[5, 6, 7, 8],
1064+
[9, 10, 11, 12]
1065+
])
1066+
df = df.set_index([0, 1]).rename_axis(['a', 'b'])
1067+
df.columns = pd.MultiIndex.from_tuples([('c', 'e'), ('d', 'f')],
1068+
names=['level_1', 'level_2'])
1069+
1070+
# test that dropping of a level in index works
1071+
expected = df.reset_index('a', drop=True)
1072+
result = df.droplevel('a', axis='index')
1073+
assert_frame_equal(result, expected)
1074+
1075+
# test that dropping of a level in columns works
1076+
expected = df.copy()
1077+
expected.columns = pd.Index(['c', 'd'], name='level_1')
1078+
result = df.droplevel('level_2', axis='columns')
1079+
assert_frame_equal(result, expected)
1080+
10591081

10601082
class TestIntervalIndex(object):
10611083

pandas/tests/series/test_alter_axes.py

+12
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,15 @@ def test_reset_index_drop_errors(self):
295295
s = pd.Series(range(4), index=pd.MultiIndex.from_product([[1, 2]] * 2))
296296
with tm.assert_raises_regex(KeyError, 'not found'):
297297
s.reset_index('wrong', drop=True)
298+
299+
def test_droplevel(self):
300+
# GH20342
301+
ser = pd.Series([1, 2, 3, 4])
302+
ser.index = pd.MultiIndex.from_arrays([(1, 2, 3, 4), (5, 6, 7, 8)],
303+
names=['a', 'b'])
304+
expected = ser.reset_index('b', drop=True)
305+
result = ser.droplevel('b', axis='index')
306+
assert_series_equal(result, expected)
307+
# test that droplevel raises ValueError on axis != 0
308+
with pytest.raises(ValueError):
309+
ser.droplevel(1, axis='columns')

0 commit comments

Comments
 (0)