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 6 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
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 here

- 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
56 changes: 56 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,62 @@ 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 indexes of levels.
Copy link
Contributor

Choose a reason for hiding this comment

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

indexes -> positional indexes


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
32 changes: 32 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,38 @@ 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'])
ser = df.iloc[:, 0]
df.columns = pd.MultiIndex.from_tuples([('c', 'e'), ('d', 'f')],
names=['level_1', 'level_2'])

# test that dropping of a level in DataFrame index works
expected_df_no_level_a_in_index = df.reset_index('a', drop=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

dont' use these long names, expected and result are much easier to grok

actual_df_no_level_a_in_index = df.droplevel('a')
assert_frame_equal(expected_df_no_level_a_in_index,
actual_df_no_level_a_in_index)

# test that dropping of a level in DataFrame columns works
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 parameterize these on axis and also try with axis='columns', and axis='index'

expected_df_no_level_2_in_columns = df.copy()
expected_df_no_level_2_in_columns.columns = pd.Index(['c', 'd'],
name='level_1')
actual_df_no_level_2_in_columns = df.droplevel('level_2', axis=1)
assert_frame_equal(expected_df_no_level_2_in_columns,
actual_df_no_level_2_in_columns)

# test that dropping of a level in Series index works
expected_ser_no_level_b_in_index = ser.reset_index('b', drop=True)
actual_ser_no_level_b_in_index = ser.droplevel('b')
Copy link
Contributor

Choose a reason for hiding this comment

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

when you move add a test that raises when axis !=0 is specified

assert_series_equal(expected_ser_no_level_b_in_index,
actual_ser_no_level_b_in_index)


class TestIntervalIndex(object):

Expand Down