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

Backport PR #25069 on branch 0.24.x (REGR: rename_axis with None should remove axis name) #25079

Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Fixed Regressions
- Fixed regression in :func:`read_sql` when passing certain queries with MySQL/pymysql (:issue:`24988`).
- Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`).
- Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`).
- Fixed regression in :meth:`Series.rename_axis` and :meth:`DataFrame.rename_axis` where passing ``None`` failed to remove the axis name (:issue:`25034`)

.. _whatsnew_0241.enhancements:

Expand Down
22 changes: 16 additions & 6 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
by : str or list of str
Name or list of names to sort by""")

# sentinel value to use as kwarg in place of None when None has special meaning
# and needs to be distinguished from a user explicitly passing None.
sentinel = object()


def _single_replace(self, to_replace, method, inplace, limit):
"""
Expand Down Expand Up @@ -290,11 +294,16 @@ def _construct_axes_dict_for_slice(self, axes=None, **kwargs):
d.update(kwargs)
return d

def _construct_axes_from_arguments(self, args, kwargs, require_all=False):
def _construct_axes_from_arguments(
self, args, kwargs, require_all=False, sentinel=None):
"""Construct and returns axes if supplied in args/kwargs.

If require_all, raise if all axis arguments are not supplied
return a tuple of (axes, kwargs).

sentinel specifies the default parameter when an axis is not
supplied; useful to distinguish when a user explicitly passes None
in scenarios where None has special meaning.
"""

# construct the args
Expand Down Expand Up @@ -322,7 +331,7 @@ def _construct_axes_from_arguments(self, args, kwargs, require_all=False):
raise TypeError("not enough/duplicate arguments "
"specified!")

axes = {a: kwargs.pop(a, None) for a in self._AXIS_ORDERS}
axes = {a: kwargs.pop(a, sentinel) for a in self._AXIS_ORDERS}
return axes, kwargs

@classmethod
Expand Down Expand Up @@ -1089,7 +1098,7 @@ def rename(self, *args, **kwargs):

@rewrite_axis_style_signature('mapper', [('copy', True),
('inplace', False)])
def rename_axis(self, mapper=None, **kwargs):
def rename_axis(self, mapper=sentinel, **kwargs):
"""
Set the name of the axis for the index or columns.

Expand Down Expand Up @@ -1218,7 +1227,8 @@ class name
cat 4 0
monkey 2 2
"""
axes, kwargs = self._construct_axes_from_arguments((), kwargs)
axes, kwargs = self._construct_axes_from_arguments(
(), kwargs, sentinel=sentinel)
copy = kwargs.pop('copy', True)
inplace = kwargs.pop('inplace', False)
axis = kwargs.pop('axis', 0)
Expand All @@ -1231,7 +1241,7 @@ class name

inplace = validate_bool_kwarg(inplace, 'inplace')

if (mapper is not None):
if (mapper is not sentinel):
# Use v0.23 behavior if a scalar or list
non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not
is_dict_like(mapper))
Expand All @@ -1254,7 +1264,7 @@ class name

for axis in lrange(self._AXIS_LEN):
v = axes.get(self._AXIS_NAMES[axis])
if v is None:
if v is sentinel:
continue
non_mapper = is_scalar(v) or (is_list_like(v) and not
is_dict_like(v))
Expand Down
20 changes: 20 additions & 0 deletions pandas/tests/frame/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,26 @@ def test_rename_axis_mapper(self):
with pytest.raises(TypeError, match='bogus'):
df.rename_axis(bogus=None)

@pytest.mark.parametrize('kwargs, rename_index, rename_columns', [
({'mapper': None, 'axis': 0}, True, False),
({'mapper': None, 'axis': 1}, False, True),
({'index': None}, True, False),
({'columns': None}, False, True),
({'index': None, 'columns': None}, True, True),
({}, False, False)])
def test_rename_axis_none(self, kwargs, rename_index, rename_columns):
# GH 25034
index = Index(list('abc'), name='foo')
columns = Index(['col1', 'col2'], name='bar')
data = np.arange(6).reshape(3, 2)
df = DataFrame(data, index, columns)

result = df.rename_axis(**kwargs)
expected_index = index.rename(None) if rename_index else index
expected_columns = columns.rename(None) if rename_columns else columns
expected = DataFrame(data, expected_index, expected_columns)
tm.assert_frame_equal(result, expected)

def test_rename_multiindex(self):

tuples_index = [('foo1', 'bar1'), ('foo2', 'bar2')]
Expand Down
11 changes: 11 additions & 0 deletions pandas/tests/series/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,17 @@ def test_rename_axis_inplace(self, datetime_series):
assert no_return is None
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize('kwargs', [{'mapper': None}, {'index': None}, {}])
def test_rename_axis_none(self, kwargs):
# GH 25034
index = Index(list('abc'), name='foo')
df = Series([1, 2, 3], index=index)

result = df.rename_axis(**kwargs)
expected_index = index.rename(None) if kwargs else index
expected = Series([1, 2, 3], index=expected_index)
tm.assert_series_equal(result, expected)

def test_set_axis_inplace_axes(self, axis_series):
# GH14636
ser = Series(np.arange(4), index=[1, 3, 5, 7], dtype='int64')
Expand Down