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

DEPR: set_axis inplace keyword #48130

Merged
merged 15 commits into from
Aug 18, 2022
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ Other Deprecations
- Deprecated the ``inplace`` keyword in :meth:`Categorical.set_ordered`, :meth:`Categorical.as_ordered`, and :meth:`Categorical.as_unordered` (:issue:`37643`)
- Deprecated setting a categorical's categories with ``cat.categories = ['a', 'b', 'c']``, use :meth:`Categorical.rename_categories` instead (:issue:`37643`)
- Deprecated unused arguments ``encoding`` and ``verbose`` in :meth:`Series.to_excel` and :meth:`DataFrame.to_excel` (:issue:`47912`)
- Deprecated the ``inplace`` keyword in :meth:`DataFrame.set_axis` and :meth:`Series.set_axis`, use ``obj = obj.set_axis(..., copy=False)`` instead (:issue:`48130`)
- Deprecated producing a single element when iterating over a :class:`DataFrameGroupBy` or a :class:`SeriesGroupBy` that has been grouped by a list of length 1; A tuple of length one will be returned instead (:issue:`42795`)
- Fixed up warning message of deprecation of :meth:`MultiIndex.lesort_depth` as public method, as the message previously referred to :meth:`MultiIndex.is_lexsorted` instead (:issue:`38701`)
- Deprecated the ``sort_columns`` argument in :meth:`DataFrame.plot` and :meth:`Series.plot` (:issue:`47563`).
Expand Down
11 changes: 5 additions & 6 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -5060,7 +5060,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: Literal[False] = ...,
inplace: Literal[False] | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> DataFrame:
...
Expand All @@ -5082,7 +5082,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: bool = ...,
inplace: bool | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> DataFrame | None:
...
Expand Down Expand Up @@ -5111,10 +5111,9 @@ def set_axis(
1 2 5
2 3 6

Now, update the labels inplace.
Now, update the labels without copying the underlying data.

>>> df.set_axis(['i', 'ii'], axis='columns', inplace=True)
>>> df
>>> df.set_axis(['i', 'ii'], axis='columns', copy=False)
i ii
0 1 4
1 2 5
Expand All @@ -5132,7 +5131,7 @@ def set_axis(
self,
labels,
axis: Axis = 0,
inplace: bool = False,
inplace: bool | lib.NoDefault = lib.no_default,
*,
copy: bool | lib.NoDefault = lib.no_default,
):
Expand Down
37 changes: 26 additions & 11 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: Literal[False] = ...,
inplace: Literal[False] | lib.NoDefault = ...,
copy: bool_t | lib.NoDefault = ...,
) -> NDFrameT:
...
Expand All @@ -737,7 +737,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: bool_t = ...,
inplace: bool_t | lib.NoDefault = ...,
copy: bool_t | lib.NoDefault = ...,
) -> NDFrameT | None:
...
Expand All @@ -747,7 +747,7 @@ def set_axis(
self: NDFrameT,
labels,
axis: Axis = 0,
inplace: bool_t = False,
inplace: bool_t | lib.NoDefault = lib.no_default,
*,
copy: bool_t | lib.NoDefault = lib.no_default,
) -> NDFrameT | None:
Expand All @@ -769,6 +769,8 @@ def set_axis(
inplace : bool, default False
Whether to return a new %(klass)s instance.

.. deprecated:: 1.5.0

copy : bool, default True
Whether to make a copy of the underlying data.

Expand All @@ -783,6 +785,17 @@ def set_axis(
--------
%(klass)s.rename_axis : Alter the name of the index%(see_also_sub)s.
"""
if inplace is not lib.no_default:
warnings.warn(
f"{type(self).__name__}.set_axis 'inplace' keyword is deprecated "
"and will be removed in a future version. Use "
"`obj = obj.set_axis(..., copy=False)` instead",
FutureWarning,
stacklevel=find_stack_level(inspect.currentframe()),
)
else:
inplace = False

if inplace:
if copy is True:
raise ValueError("Cannot specify both inplace=True and copy=True")
Expand All @@ -795,14 +808,13 @@ def set_axis(

@final
def _set_axis_nocheck(self, labels, axis: Axis, inplace: bool_t, copy: bool_t):
# NDFrame.rename with inplace=False calls set_axis(inplace=True) on a copy.
if inplace:
setattr(self, self._get_axis_name(axis), labels)
else:
# With copy=False, we create a new object but don't copy the
# underlying data.
obj = self.copy(deep=copy)
obj.set_axis(labels, axis=axis, inplace=True)
setattr(obj, obj._get_axis_name(axis), labels)
return obj

def _set_axis(self, axis: int, labels: AnyArrayLike | list) -> None:
Expand Down Expand Up @@ -904,7 +916,7 @@ def droplevel(self: NDFrameT, level: IndexLabel, axis: Axis = 0) -> NDFrameT:
"""
labels = self._get_axis(axis)
new_labels = labels.droplevel(level)
return self.set_axis(new_labels, axis=axis, inplace=False)
return self.set_axis(new_labels, axis=axis)

def pop(self, item: Hashable) -> Series | Any:
result = self[item]
Expand Down Expand Up @@ -1363,7 +1375,11 @@ def _set_axis_name(self, name, axis=0, inplace=False):

inplace = validate_bool_kwarg(inplace, "inplace")
renamed = self if inplace else self.copy()
renamed.set_axis(idx, axis=axis, inplace=True)
if axis == 0:
renamed.index = idx
else:
renamed.columns = idx

if not inplace:
return renamed

Expand Down Expand Up @@ -10205,8 +10221,7 @@ def slice_shift(self: NDFrameT, periods: int = 1, axis=0) -> NDFrameT:

new_obj = self._slice(vslicer, axis=axis)
shifted_axis = self._get_axis(axis)[islicer]
new_obj.set_axis(shifted_axis, axis=axis, inplace=True)

new_obj = new_obj.set_axis(shifted_axis, axis=axis, copy=False)
return new_obj.__finalize__(self, method="slice_shift")

@final
Expand Down Expand Up @@ -10465,7 +10480,7 @@ def _tz_convert(ax, tz):
ax = _tz_convert(ax, tz)

result = self.copy(deep=copy)
result = result.set_axis(ax, axis=axis, inplace=False)
result = result.set_axis(ax, axis=axis, copy=False)
return result.__finalize__(self, method="tz_convert")

@final
Expand Down Expand Up @@ -10635,7 +10650,7 @@ def _tz_localize(ax, tz, ambiguous, nonexistent):
ax = _tz_localize(ax, tz, ambiguous, nonexistent)

result = self.copy(deep=copy)
result = result.set_axis(ax, axis=axis, inplace=False)
result = result.set_axis(ax, axis=axis, copy=False)
return result.__finalize__(self, method="tz_localize")

# ----------------------------------------------------------------------
Expand Down
9 changes: 5 additions & 4 deletions pandas/core/groupby/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,21 +1200,22 @@ def _set_result_index_ordered(
# set the result index on the passed values object and
# return the new object, xref 8046

obj_axis = self.obj._get_axis(self.axis)

if self.grouper.is_monotonic and not self.grouper.has_dropped_na:
# shortcut if we have an already ordered grouper
result.set_axis(self.obj._get_axis(self.axis), axis=self.axis, inplace=True)
result = result.set_axis(obj_axis, axis=self.axis, copy=False)
return result

# row order is scrambled => sort the rows by position in original index
original_positions = Index(self.grouper.result_ilocs())
result.set_axis(original_positions, axis=self.axis, inplace=True)
result = result.set_axis(original_positions, axis=self.axis, copy=False)
result = result.sort_index(axis=self.axis)
obj_axis = self.obj._get_axis(self.axis)
if self.grouper.has_dropped_na:
# Add back in any missing rows due to dropna - index here is integral
# with values referring to the row of the input so can use RangeIndex
result = result.reindex(RangeIndex(len(obj_axis)), axis=self.axis)
result.set_axis(obj_axis, axis=self.axis, inplace=True)
result = result.set_axis(obj_axis, axis=self.axis, copy=False)

return result

Expand Down
6 changes: 3 additions & 3 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4980,7 +4980,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: Literal[False] = ...,
inplace: Literal[False] | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> Series:
...
Expand All @@ -5002,7 +5002,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: bool = ...,
inplace: bool | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> Series | None:
...
Expand Down Expand Up @@ -5038,7 +5038,7 @@ def set_axis( # type: ignore[override]
self,
labels,
axis: Axis = 0,
inplace: bool = False,
inplace: bool | lib.NoDefault = lib.no_default,
copy: bool | lib.NoDefault = lib.no_default,
) -> Series | None:
return super().set_axis(labels, axis=axis, inplace=inplace, copy=copy)
Expand Down
17 changes: 12 additions & 5 deletions pandas/tests/frame/methods/test_set_axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def test_set_axis(self, obj):
expected.index = new_index

# inplace=False
result = obj.set_axis(new_index, axis=0, inplace=False)
msg = "set_axis 'inplace' keyword is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
result = obj.set_axis(new_index, axis=0, inplace=False)
tm.assert_equal(expected, result)

def test_set_axis_copy(self, obj):
Expand All @@ -35,7 +37,8 @@ def test_set_axis_copy(self, obj):
with pytest.raises(
ValueError, match="Cannot specify both inplace=True and copy=True"
):
obj.set_axis(new_index, axis=0, inplace=True, copy=True)
with tm.assert_produces_warning(FutureWarning):
obj.set_axis(new_index, axis=0, inplace=True, copy=True)

result = obj.set_axis(new_index, axis=0, copy=True)
tm.assert_equal(expected, result)
Expand Down Expand Up @@ -75,7 +78,8 @@ def test_set_axis_copy(self, obj):
)

# Do this last since it alters obj inplace
res = obj.set_axis(new_index, inplace=True, copy=False)
with tm.assert_produces_warning(FutureWarning):
res = obj.set_axis(new_index, inplace=True, copy=False)
assert res is None
tm.assert_equal(expected, obj)
# check we did NOT make a copy
Expand Down Expand Up @@ -103,7 +107,8 @@ def test_set_axis_inplace_axis(self, axis, obj):
expected.columns = new_index

result = obj.copy()
result.set_axis(new_index, axis=axis, inplace=True)
with tm.assert_produces_warning(FutureWarning):
result.set_axis(new_index, axis=axis, inplace=True)
tm.assert_equal(result, expected)

def test_set_axis_unnamed_kwarg_warns(self, obj):
Expand All @@ -113,7 +118,9 @@ def test_set_axis_unnamed_kwarg_warns(self, obj):
expected = obj.copy()
expected.index = new_index

with tm.assert_produces_warning(None):
with tm.assert_produces_warning(
FutureWarning, match="set_axis 'inplace' keyword"
):
result = obj.set_axis(new_index, inplace=False)
tm.assert_equal(result, expected)

Expand Down
12 changes: 10 additions & 2 deletions pandas/tests/generic/test_duplicate_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,19 @@ def test_inplace_raises(method, frame_only):
s.flags.allows_duplicate_labels = False
msg = "Cannot specify"

warn_msg = "Series.set_axis 'inplace' keyword"
if "set_axis" in str(method):
warn = FutureWarning
else:
warn = None

with pytest.raises(ValueError, match=msg):
method(df)
with tm.assert_produces_warning(warn, match=warn_msg):
method(df)
if not frame_only:
with pytest.raises(ValueError, match=msg):
method(s)
with tm.assert_produces_warning(warn, match=warn_msg):
method(s)


def test_pickle():
Expand Down