Skip to content

Commit ae16bf9

Browse files
jorisvandenbosscheTomAugspurger
authored andcommitted
API: harmonize drop/reindex/rename args (GH12392) - drop (#17644)
* API: harmonize drop/reindex/rename args (GH12392) - drop * fixups * add versionadded
1 parent c95eb38 commit ae16bf9

File tree

3 files changed

+129
-26
lines changed

3 files changed

+129
-26
lines changed

doc/source/whatsnew/v0.21.0.txt

+18
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ This does not raise any obvious exceptions, but also does not create a new colum
9191

9292
Setting a list-like data structure into a new attribute now raise a ``UserWarning`` about the potential for unexpected behavior. See :ref:`Attribute Access <indexing.attribute_access>`.
9393

94+
``drop`` now also accepts index/columns keywords
95+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
96+
97+
The :meth:`~DataFrame.drop` method has gained ``index``/``columns`` keywords as an
98+
alternative to specify the ``axis`` and to make it similar in usage to ``reindex``
99+
(:issue:`12392`).
100+
101+
For example:
102+
103+
.. ipython:: python
104+
105+
df = pd.DataFrame(np.arange(8).reshape(2,4),
106+
columns=['A', 'B', 'C', 'D'])
107+
df
108+
df.drop(['B', 'C'], axis=1)
109+
# the following is now equivalent
110+
df.drop(columns=['B', 'C'])
111+
94112
.. _whatsnew_0210.enhancements.categorical_dtype:
95113

96114
``CategoricalDtype`` for specifying categoricals

pandas/core/generic.py

+76-26
Original file line numberDiff line numberDiff line change
@@ -2333,14 +2333,23 @@ def reindex_like(self, other, method=None, copy=True, limit=None,
23332333

23342334
return self.reindex(**d)
23352335

2336-
def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
2336+
def drop(self, labels=None, axis=0, index=None, columns=None, level=None,
2337+
inplace=False, errors='raise'):
23372338
"""
23382339
Return new object with labels in requested axis removed.
23392340
23402341
Parameters
23412342
----------
23422343
labels : single label or list-like
2344+
Index or column labels to drop.
23432345
axis : int or axis name
2346+
Whether to drop labels from the index (0 / 'index') or
2347+
columns (1 / 'columns').
2348+
index, columns : single label or list-like
2349+
Alternative to specifying `axis` (``labels, axis=1`` is
2350+
equivalent to ``columns=labels``).
2351+
2352+
.. versionadded:: 0.21.0
23442353
level : int or level name, default None
23452354
For MultiIndex
23462355
inplace : bool, default False
@@ -2354,36 +2363,80 @@ def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
23542363
23552364
Examples
23562365
--------
2357-
>>> df = pd.DataFrame([[1, 2, 3, 4],
2358-
... [5, 6, 7, 8],
2359-
... [9, 1, 2, 3],
2360-
... [4, 5, 6, 7]
2361-
... ],
2362-
... columns=list('ABCD'))
2366+
>>> df = pd.DataFrame(np.arange(12).reshape(3,4),
2367+
columns=['A', 'B', 'C', 'D'])
23632368
>>> df
2364-
A B C D
2365-
0 1 2 3 4
2366-
1 5 6 7 8
2367-
2 9 1 2 3
2368-
3 4 5 6 7
2369+
A B C D
2370+
0 0 1 2 3
2371+
1 4 5 6 7
2372+
2 8 9 10 11
2373+
2374+
Drop columns
2375+
2376+
>>> df.drop(['B', 'C'], axis=1)
2377+
A D
2378+
0 0 3
2379+
1 4 7
2380+
2 8 11
2381+
2382+
>>> df.drop(columns=['B', 'C'])
2383+
A D
2384+
0 0 3
2385+
1 4 7
2386+
2 8 11
23692387
23702388
Drop a row by index
23712389
23722390
>>> df.drop([0, 1])
2373-
A B C D
2374-
2 9 1 2 3
2375-
3 4 5 6 7
2391+
A B C D
2392+
2 8 9 10 11
23762393
2377-
Drop columns
2394+
Notes
2395+
-----
2396+
Specifying both `labels` and `index` or `columns` will raise a
2397+
ValueError.
23782398
2379-
>>> df.drop(['A', 'B'], axis=1)
2380-
C D
2381-
0 3 4
2382-
1 7 8
2383-
2 2 3
2384-
3 6 7
23852399
"""
23862400
inplace = validate_bool_kwarg(inplace, 'inplace')
2401+
2402+
if labels is not None:
2403+
if index is not None or columns is not None:
2404+
raise ValueError("Cannot specify both 'labels' and "
2405+
"'index'/'columns'")
2406+
axis_name = self._get_axis_name(axis)
2407+
axes = {axis_name: labels}
2408+
elif index is not None or columns is not None:
2409+
axes, _ = self._construct_axes_from_arguments((index, columns), {})
2410+
else:
2411+
raise ValueError("Need to specify at least one of 'labels', "
2412+
"'index' or 'columns'")
2413+
2414+
obj = self
2415+
2416+
for axis, labels in axes.items():
2417+
if labels is not None:
2418+
obj = obj._drop_axis(labels, axis, level=level, errors=errors)
2419+
2420+
if inplace:
2421+
self._update_inplace(obj)
2422+
else:
2423+
return obj
2424+
2425+
def _drop_axis(self, labels, axis, level=None, errors='raise'):
2426+
"""
2427+
Drop labels from specified axis. Used in the ``drop`` method
2428+
internally.
2429+
2430+
Parameters
2431+
----------
2432+
labels : single label or list-like
2433+
axis : int or axis name
2434+
level : int or level name, default None
2435+
For MultiIndex
2436+
errors : {'ignore', 'raise'}, default 'raise'
2437+
If 'ignore', suppress error and existing labels are dropped.
2438+
2439+
"""
23872440
axis = self._get_axis_number(axis)
23882441
axis_name = self._get_axis_name(axis)
23892442
axis, axis_ = self._get_axis(axis), axis
@@ -2416,10 +2469,7 @@ def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
24162469

24172470
result = self.loc[tuple(slicer)]
24182471

2419-
if inplace:
2420-
self._update_inplace(result)
2421-
else:
2422-
return result
2472+
return result
24232473

24242474
def _update_inplace(self, result, verify_is_copy=True):
24252475
"""

pandas/tests/frame/test_axis_select_reindex.py

+35
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,41 @@ def test_drop_multiindex_not_lexsorted(self):
146146

147147
tm.assert_frame_equal(result, expected)
148148

149+
def test_drop_api_equivalence(self):
150+
# equivalence of the labels/axis and index/columns API's (GH12392)
151+
df = DataFrame([[1, 2, 3], [3, 4, 5], [5, 6, 7]],
152+
index=['a', 'b', 'c'],
153+
columns=['d', 'e', 'f'])
154+
155+
res1 = df.drop('a')
156+
res2 = df.drop(index='a')
157+
tm.assert_frame_equal(res1, res2)
158+
159+
res1 = df.drop('d', 1)
160+
res2 = df.drop(columns='d')
161+
tm.assert_frame_equal(res1, res2)
162+
163+
res1 = df.drop(labels='e', axis=1)
164+
res2 = df.drop(columns='e')
165+
tm.assert_frame_equal(res1, res2)
166+
167+
res1 = df.drop(['a'], axis=0)
168+
res2 = df.drop(index=['a'])
169+
tm.assert_frame_equal(res1, res2)
170+
171+
res1 = df.drop(['a'], axis=0).drop(['d'], axis=1)
172+
res2 = df.drop(index=['a'], columns=['d'])
173+
tm.assert_frame_equal(res1, res2)
174+
175+
with pytest.raises(ValueError):
176+
df.drop(labels='a', index='b')
177+
178+
with pytest.raises(ValueError):
179+
df.drop(labels='a', columns='b')
180+
181+
with pytest.raises(ValueError):
182+
df.drop(axis=1)
183+
149184
def test_merge_join_different_levels(self):
150185
# GH 9455
151186

0 commit comments

Comments
 (0)