Skip to content
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

ENH: Add errors parameter to DataFrame.rename #25535

Merged
32 changes: 24 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,45 @@ 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
WillAyd marked this conversation as resolved.
Show resolved Hide resolved
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 'ignore', suppress error and existing labels are renamed.
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
DataFrame
DataFrame with the renamed axis labels.

Raises
------
KeyError
If any of the labels is not found in the selected axis.

See Also
--------
DataFrame.rename_axis
DataFrame.rename_axis: Set the name of the axis for the index or
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved
columns.
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved

Examples
--------
Expand Down
19 changes: 18 additions & 1 deletion pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,11 +981,18 @@ 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'
WillAyd marked this conversation as resolved.
Show resolved Hide resolved
If 'ignore', suppress error and existing labels are renamed.
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
renamed : %(klass)s (new object)

Raises
------
KeyError
If any of the labels is not found in the selected axis.
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved

See Also
--------
NDFrame.rename_axis
Expand Down Expand Up @@ -1065,6 +1072,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 +1093,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)
missing_labels = [label for index, label in enumerate(v)
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved
if indexer[index] == -1]
if errors == 'raise' and len(missing_labels) > 0:
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
25 changes: 24 additions & 1 deletion pandas/tests/frame/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,29 @@ def test_rename_bug2(self):
columns=["a"])
tm.assert_frame_equal(df, expected)

def test_rename_errors(self):
# GH 13473
# rename now works with errors parameter

# Error has to be thrown and is thrown
df = DataFrame(columns=['A', 'B', 'C', 'D'])
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved
with pytest.raises(KeyError):
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved
df.rename(columns={'A': 'a', 'E': 'e'}, errors='raise')

# Error should be ignored
renamed = df.rename(columns={'A': 'a', 'E': 'e'})
MaxVanDeursen marked this conversation as resolved.
Show resolved Hide resolved
expected = DataFrame(columns=['a', 'B', 'C', 'D'])
tm.assert_frame_equal(renamed, expected)

# Correct behaviour with raising errors.
renamed = df.rename(columns={'A': 'a'}, errors='raise')
expected = DataFrame(columns=['a', 'B', 'C', 'D'])
tm.assert_frame_equal(renamed, expected)

renamed = df.rename(columns=str.lower, errors='raise')
expected = DataFrame(columns=['a', 'b', 'c', 'd'])
tm.assert_frame_equal(renamed, 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 @@ -1328,7 +1351,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