Skip to content

Commit

Permalink
ENH: Add errors parameter to DataFrame.rename (#25535)
Browse files Browse the repository at this point in the history
* ENH: GH13473 Add errors parameter to DataFrame.rename
  • Loading branch information
MaxVanDeursen authored and TomAugspurger committed Mar 5, 2019
1 parent 221be3b commit 615fbb3
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 11 deletions.
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Other Enhancements
- ``Series.str`` has gained :meth:`Series.str.casefold` method to removes all case distinctions present in a string (:issue:`25405`)
- :meth:`DataFrame.set_index` now works for instances of ``abc.Iterator``, provided their output is of the same length as the calling frame (:issue:`22484`, :issue:`24984`)
- :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`)
-
- :meth:`DataFrame.rename` now supports the ``errors`` argument to raise errors when attempting to rename nonexistent keys (:issue:`13473`)

.. _whatsnew_0250.api_breaking:

Expand Down
40 changes: 32 additions & 8 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -3911,7 +3911,8 @@ def drop(self, labels=None, axis=0, index=None, columns=None,

@rewrite_axis_style_signature('mapper', [('copy', True),
('inplace', False),
('level', None)])
('level', None),
('errors', 'ignore')])
def rename(self, *args, **kwargs):
"""
Alter axes labels.
Expand All @@ -3924,30 +3925,49 @@ def rename(self, *args, **kwargs):
Parameters
----------
mapper, index, columns : dict-like or function, optional
dict-like or functions transformations to apply to
mapper : dict-like or function
Dict-like or functions transformations to apply to
that axis' values. Use either ``mapper`` and ``axis`` to
specify the axis to target with ``mapper``, or ``index`` and
``columns``.
axis : int or str, optional
index : dict-like or function
Alternative to specifying axis (``mapper, axis=0``
is equivalent to ``index=mapper``).
columns : dict-like or function
Alternative to specifying axis (``mapper, axis=1``
is equivalent to ``columns=mapper``).
axis : int or str
Axis to target with ``mapper``. Can be either the axis name
('index', 'columns') or number (0, 1). The default is 'index'.
copy : boolean, default True
Also copy underlying data
inplace : boolean, default False
copy : bool, default True
Also copy underlying data.
inplace : bool, default False
Whether to return a new DataFrame. If True then value of copy is
ignored.
level : int or level name, default None
In case of a MultiIndex, only rename labels in the specified
level.
errors : {'ignore', 'raise'}, default 'ignore'
If 'raise', raise a `KeyError` when a dict-like `mapper`, `index`,
or `columns` contains labels that are not present in the Index
being transformed.
If 'ignore', existing keys will be renamed and extra keys will be
ignored.
Returns
-------
DataFrame
DataFrame with the renamed axis labels.
Raises
------
KeyError
If any of the labels is not found in the selected axis and
"errors='raise'".
See Also
--------
DataFrame.rename_axis
DataFrame.rename_axis : Set the name of the axis.
Examples
--------
Expand All @@ -3973,6 +3993,10 @@ def rename(self, *args, **kwargs):
1 2 5
2 3 6
>>> df.rename(index=str, columns={"A": "a", "C": "c"}, errors="raise")
Traceback (most recent call last):
KeyError: ['C'] not found in axis
Using axis-style parameters
>>> df.rename(str.lower, axis='columns')
Expand Down
24 changes: 23 additions & 1 deletion pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,11 +981,23 @@ def rename(self, *args, **kwargs):
level : int or level name, default None
In case of a MultiIndex, only rename labels in the specified
level.
errors : {'ignore', 'raise'}, default 'ignore'
If 'raise', raise a `KeyError` when a dict-like `mapper`, `index`,
or `columns` contains labels that are not present in the Index
being transformed.
If 'ignore', existing keys will be renamed and extra keys will be
ignored.
Returns
-------
renamed : %(klass)s (new object)
Raises
------
KeyError
If any of the labels is not found in the selected axis and
"errors='raise'".
See Also
--------
NDFrame.rename_axis
Expand Down Expand Up @@ -1065,6 +1077,7 @@ def rename(self, *args, **kwargs):
inplace = kwargs.pop('inplace', False)
level = kwargs.pop('level', None)
axis = kwargs.pop('axis', None)
errors = kwargs.pop('errors', 'ignore')
if axis is not None:
# Validate the axis
self._get_axis_number(axis)
Expand All @@ -1085,10 +1098,19 @@ def rename(self, *args, **kwargs):
if v is None:
continue
f = com._get_rename_function(v)

baxis = self._get_block_manager_axis(axis)
if level is not None:
level = self.axes[axis]._get_level_number(level)

# GH 13473
if not callable(v):
indexer = self.axes[axis].get_indexer_for(v)
if errors == 'raise' and len(indexer[indexer == -1]):
missing_labels = [label for index, label in enumerate(v)
if indexer[index] == -1]
raise KeyError('{} not found in axis'
.format(missing_labels))

result._data = result._data.rename_axis(f, axis=baxis, copy=copy,
level=level)
result._clear_item_cache()
Expand Down
19 changes: 18 additions & 1 deletion pandas/tests/frame/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,23 @@ def test_rename_bug2(self):
columns=["a"])
tm.assert_frame_equal(df, expected)

def test_rename_errors_raises(self):
df = DataFrame(columns=['A', 'B', 'C', 'D'])
with pytest.raises(KeyError, match='\'E\'] not found in axis'):
df.rename(columns={'A': 'a', 'E': 'e'}, errors='raise')

@pytest.mark.parametrize('mapper, errors, expected_columns', [
({'A': 'a', 'E': 'e'}, 'ignore', ['a', 'B', 'C', 'D']),
({'A': 'a'}, 'raise', ['a', 'B', 'C', 'D']),
(str.lower, 'raise', ['a', 'b', 'c', 'd'])])
def test_rename_errors(self, mapper, errors, expected_columns):
# GH 13473
# rename now works with errors parameter
df = DataFrame(columns=['A', 'B', 'C', 'D'])
result = df.rename(columns=mapper, errors=errors)
expected = DataFrame(columns=expected_columns)
tm.assert_frame_equal(result, expected)

def test_reorder_levels(self):
index = MultiIndex(levels=[['bar'], ['one', 'two', 'three'], [0, 1]],
codes=[[0, 0, 0, 0, 0, 0],
Expand Down Expand Up @@ -1329,7 +1346,7 @@ def test_rename_signature(self):
sig = inspect.signature(DataFrame.rename)
parameters = set(sig.parameters)
assert parameters == {"self", "mapper", "index", "columns", "axis",
"inplace", "copy", "level"}
"inplace", "copy", "level", "errors"}

@pytest.mark.skipif(PY2, reason="inspect.signature")
def test_reindex_signature(self):
Expand Down

0 comments on commit 615fbb3

Please sign in to comment.