Skip to content

Commit

Permalink
DEPR: set_axis inplace keyword (pandas-dev#48130)
Browse files Browse the repository at this point in the history
* ENH: copy keyword to set_axis

* GH ref

* fix test

* fix test

* troubleshoot mypy

* mypy fixup

* test inplace=True/copy=False case

* DEPR: inplace keyword in set_axis

* GH ref
  • Loading branch information
jbrockmendel authored and noatamir committed Nov 9, 2022
1 parent 5b44c8f commit f03c9fb
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 31 deletions.
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 @@ -841,6 +841,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 ``inplace`` keyword in :meth:`DataFrame.set_index`, use ``df = df.set_index(..., copy=False)`` instead (:issue:`48115`)
Expand Down
11 changes: 5 additions & 6 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -5061,7 +5061,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: Literal[False] = ...,
inplace: Literal[False] | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> DataFrame:
...
Expand All @@ -5083,7 +5083,7 @@ def set_axis(
labels,
*,
axis: Axis = ...,
inplace: bool = ...,
inplace: bool | lib.NoDefault = ...,
copy: bool | lib.NoDefault = ...,
) -> DataFrame | None:
...
Expand Down Expand Up @@ -5112,10 +5112,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 @@ -5133,7 +5132,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

0 comments on commit f03c9fb

Please sign in to comment.