Skip to content

API: Add DataFrame.droplevel #21871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ Reindexing / Selection / Label manipulation

Series.align
Series.drop
Series.droplevel
Series.drop_duplicates
Series.duplicated
Series.equals
Expand Down Expand Up @@ -1063,6 +1064,7 @@ Reshaping, sorting, transposing
.. autosummary::
:toctree: generated/

DataFrame.droplevel
DataFrame.pivot
DataFrame.pivot_table
DataFrame.reorder_levels
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Other Enhancements
- :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`)
- :func:`to_csv` now supports ``compression`` keyword when a file handle is passed. (:issue:`21227`)
- :meth:`Index.droplevel` is now implemented also for flat indexes, for compatibility with :class:`MultiIndex` (:issue:`21115`)
- :meth:`Series.droplevel` and `DataFrame.droplevel` are now implemented (:issue:`20342`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need ref for DataFrame.droplevel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please explain more, not sure what you mean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have

`DataFrame.droplevel`

Should be

:meth:`DataFrame.droplevel`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!

- Added support for reading from Google Cloud Storage via the ``gcsfs`` library (:issue:`19454`)
- :func:`to_gbq` and :func:`read_gbq` signature and documentation updated to
reflect changes from the `Pandas-GBQ library version 0.5.0
Expand Down
58 changes: 58 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,64 @@ def swapaxes(self, axis1, axis2, copy=True):

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

def droplevel(self, level, axis=0):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anyone think we should add inplace to make this consistent, with other axis ops (e.g. reset_index / set_index)?

cc @TomAugspurger

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense to me to expose inplace!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say leave it out for now, unless someone asks for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, will do!

"""Return DataFrame with requested index / column level(s) removed.

.. versionadded:: 0.24.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need a blank line here? @jorisvandenbossche


Parameters
----------
level : int, str, or list-like
If a string is given, must be the name of a level
If list-like, elements must be names or positional indexes
of levels.

axis : {0 or 'index', 1 or 'columns'}, default 0


Returns
-------
DataFrame.droplevel()

Examples
--------
>>> df = pd.DataFrame([
...: [1, 2, 3, 4],
...: [5, 6, 7, 8],
...: [9, 10, 11, 12]
...: ]).set_index([0, 1]).rename_axis(['a', 'b'])
>>> df.columns = pd.MultiIndex.from_tuples([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add blank lines in between here

...: ('c', 'e'), ('d', 'f')
...:], names=['level_1', 'level_2'])
>>> df
level_1 c d
level_2 e f
a b
1 2 3 4
5 6 7 8
9 10 11 12

>>> df.droplevel('a')
level_1 c d
level_2 e f
b
2 3 4
6 7 8
10 11 12

>>> df.droplevel('level2', axis=1)
level_1 c d
a b
1 2 3 4
5 6 7 8
9 10 11 12

"""
labels = self._get_axis(axis)
new_labels = labels.droplevel(level)
result = self.set_axis(new_labels, axis=axis, inplace=False)
return result

def pop(self, item):
"""
Return item and drop from frame. Raise KeyError if not found.
Expand Down
22 changes: 22 additions & 0 deletions pandas/tests/frame/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,28 @@ def test_reindex_signature(self):
"limit", "copy", "level", "method",
"fill_value", "tolerance"}

def test_droplevel(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add the issue number as a comment

# GH20342
df = pd.DataFrame([
[1, 2, 3, 4],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add tests for series as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did, on lines 1085-1089 :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh not in THIS test. pls separate out and put in tests/series/test_alter_axes

[5, 6, 7, 8],
[9, 10, 11, 12]
])
df = df.set_index([0, 1]).rename_axis(['a', 'b'])
df.columns = pd.MultiIndex.from_tuples([('c', 'e'), ('d', 'f')],
names=['level_1', 'level_2'])

# test that dropping of a level in index works
expected = df.reset_index('a', drop=True)
result = df.droplevel('a', axis='index')
assert_frame_equal(result, expected)

# test that dropping of a level in columns works
expected = df.copy()
expected.columns = pd.Index(['c', 'd'], name='level_1')
result = df.droplevel('level_2', axis='columns')
assert_frame_equal(result, expected)


class TestIntervalIndex(object):

Expand Down
12 changes: 12 additions & 0 deletions pandas/tests/series/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,15 @@ def test_reset_index_drop_errors(self):
s = pd.Series(range(4), index=pd.MultiIndex.from_product([[1, 2]] * 2))
with tm.assert_raises_regex(KeyError, 'not found'):
s.reset_index('wrong', drop=True)

def test_droplevel(self):
# GH20342
ser = pd.Series([1, 2, 3, 4])
ser.index = pd.MultiIndex.from_arrays([(1, 2, 3, 4), (5, 6, 7, 8)],
names=['a', 'b'])
expected = ser.reset_index('b', drop=True)
result = ser.droplevel('b', axis='index')
assert_series_equal(result, expected)
# test that droplevel raises ValueError on axis != 0
with pytest.raises(ValueError):
ser.droplevel(1, axis='columns')