Skip to content

Commit

Permalink
DEP: Enforce set_values and set_codes inplace and positional args dep…
Browse files Browse the repository at this point in the history
…recation (pandas-dev#49084)

* DEP: Enforce set_values and set_codes inplace and positional args deprecation

* Fix

* Add whatsnew

* Add test
  • Loading branch information
phofl authored and noatamir committed Nov 9, 2022
1 parent ce1ae89 commit 80bdd41
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 220 deletions.
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v2.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ Removal of prior version deprecations/changes
- Remove argument ``squeeze`` from :meth:`DataFrame.groupby` and :meth:`Series.groupby` (:issue:`32380`)
- Removed ``keep_tz`` argument in :meth:`DatetimeIndex.to_series` (:issue:`29731`)
- Remove arguments ``names`` and ``dtype`` from :meth:`Index.copy` and ``levels`` and ``codes`` from :meth:`MultiIndex.copy` (:issue:`35853`, :issue:`36685`)
- Remove argument ``inplace`` from :meth:`MultiIndex.set_levels` and :meth:`MultiIndex.set_codes` (:issue:`35626`)
- Disallow passing positional arguments to :meth:`MultiIndex.set_levels` and :meth:`MultiIndex.set_codes` (:issue:`41485`)
- Removed argument ``try_cast`` from :meth:`DataFrame.mask`, :meth:`DataFrame.where`, :meth:`Series.mask` and :meth:`Series.where` (:issue:`38836`)
- Disallow passing non-round floats to :class:`Timestamp` with ``unit="M"`` or ``unit="Y"`` (:issue:`47266`)
- Removed the ``numeric_only`` keyword from :meth:`Categorical.min` and :meth:`Categorical.max` in favor of ``skipna`` (:issue:`48821`)
Expand Down
50 changes: 6 additions & 44 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,10 +852,7 @@ def _set_levels(

self._reset_cache()

@deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "levels"])
def set_levels(
self, levels, level=None, inplace=None, verify_integrity: bool = True
):
def set_levels(self, levels, *, level=None, verify_integrity: bool = True):
"""
Set new levels on MultiIndex. Defaults to returning new index.
Expand All @@ -865,10 +862,6 @@ def set_levels(
New level(s) to apply.
level : int, level name, or sequence of int/level names (default None)
Level(s) to set (None for all levels).
inplace : bool
If True, mutates in place.
.. deprecated:: 1.2.0
verify_integrity : bool, default True
If True, checks that levels and codes are compatible.
Expand Down Expand Up @@ -940,30 +933,17 @@ def set_levels(
>>> idx.set_levels([['a', 'b', 'c'], [1, 2, 3, 4]], level=[0, 1]).levels
FrozenList([['a', 'b', 'c'], [1, 2, 3, 4]])
"""
if inplace is not None:
warnings.warn(
"inplace is deprecated and will be removed in a future version.",
FutureWarning,
stacklevel=find_stack_level(),
)
else:
inplace = False

if is_list_like(levels) and not isinstance(levels, Index):
levels = list(levels)

level, levels = _require_listlike(level, levels, "Levels")

if inplace:
idx = self
else:
idx = self._view()
idx = self._view()
idx._reset_identity()
idx._set_levels(
levels, level=level, validate=True, verify_integrity=verify_integrity
)
if not inplace:
return idx
return idx

@property
def nlevels(self) -> int:
Expand Down Expand Up @@ -1041,8 +1021,7 @@ def _set_codes(

self._reset_cache()

@deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "codes"])
def set_codes(self, codes, level=None, inplace=None, verify_integrity: bool = True):
def set_codes(self, codes, *, level=None, verify_integrity: bool = True):
"""
Set new codes on MultiIndex. Defaults to returning new index.
Expand All @@ -1052,10 +1031,6 @@ def set_codes(self, codes, level=None, inplace=None, verify_integrity: bool = Tr
New codes to apply.
level : int, level name, or sequence of int/level names (default None)
Level(s) to set (None for all levels).
inplace : bool
If True, mutates in place.
.. deprecated:: 1.2.0
verify_integrity : bool, default True
If True, checks that levels and codes are compatible.
Expand Down Expand Up @@ -1101,25 +1076,12 @@ def set_codes(self, codes, level=None, inplace=None, verify_integrity: bool = Tr
(1, 'two')],
names=['foo', 'bar'])
"""
if inplace is not None:
warnings.warn(
"inplace is deprecated and will be removed in a future version.",
FutureWarning,
stacklevel=find_stack_level(),
)
else:
inplace = False

level, codes = _require_listlike(level, codes, "Codes")

if inplace:
idx = self
else:
idx = self._view()
idx = self._view()
idx._reset_identity()
idx._set_codes(codes, level=level, verify_integrity=verify_integrity)
if not inplace:
return idx
return idx

# --------------------------------------------------------------------
# Index Internals
Expand Down
17 changes: 2 additions & 15 deletions pandas/tests/indexes/multi/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,12 @@ def test_inplace_mutation_resets_values():
new_vals = mi1.set_levels(levels2).values
tm.assert_almost_equal(vals2, new_vals)

# Non-inplace doesn't drop _values from _cache [implementation detail]
# Doesn't drop _values from _cache [implementation detail]
tm.assert_almost_equal(mi1._cache["_values"], vals)

# ...and values is still same too
tm.assert_almost_equal(mi1.values, vals)

# Inplace should drop _values from _cache
with tm.assert_produces_warning(FutureWarning):
mi1.set_levels(levels2, inplace=True)
assert "_values" not in mi1._cache
tm.assert_almost_equal(mi1.values, vals2)

# Make sure label setting works too
codes2 = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
exp_values = np.empty((6,), dtype=object)
Expand All @@ -84,15 +78,8 @@ def test_inplace_mutation_resets_values():
new_values = new_mi.values
assert "_values" in new_mi._cache

# Not inplace shouldn't change
# Shouldn't change cache
tm.assert_almost_equal(mi2._cache["_values"], vals2)

# Should have correct values
tm.assert_almost_equal(exp_values, new_values)

# ...and again setting inplace should drop _values from _cache, etc
with tm.assert_produces_warning(FutureWarning):
mi2.set_codes(codes2, inplace=True)
assert "_values" not in mi2._cache
tm.assert_almost_equal(mi2.values, new_values)
assert "_values" in mi2._cache
3 changes: 1 addition & 2 deletions pandas/tests/indexes/multi/test_duplicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ def test_duplicate_multiindex_codes():
mi = MultiIndex.from_arrays([["A", "A", "B", "B", "B"], [1, 2, 1, 2, 3]])
msg = r"Level values must be unique: \[[AB', ]+\] on level 0"
with pytest.raises(ValueError, match=msg):
with tm.assert_produces_warning(FutureWarning):
mi.set_levels([["A", "B", "A", "A", "B"], [2, 1, 3, -2, 5]], inplace=True)
mi.set_levels([["A", "B", "A", "A", "B"], [2, 1, 3, -2, 5]])


@pytest.mark.parametrize("names", [["a", "b", "a"], [1, 1, 2], [1, "a", 1]])
Expand Down
9 changes: 2 additions & 7 deletions pandas/tests/indexes/multi/test_equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,6 @@ def test_is_():
assert mi.is_(mi2)

assert not mi.is_(mi.set_names(["C", "D"]))
mi2 = mi.view()
mi2.set_names(["E", "F"], inplace=True)
assert mi.is_(mi2)
# levels are inherent properties, they change identity
mi3 = mi2.set_levels([list(range(10)), list(range(10))])
assert not mi3.is_(mi2)
Expand All @@ -254,12 +251,10 @@ def test_is_():
mi4 = mi3.view()

# GH 17464 - Remove duplicate MultiIndex levels
with tm.assert_produces_warning(FutureWarning):
mi4.set_levels([list(range(10)), list(range(10))], inplace=True)
mi4 = mi4.set_levels([list(range(10)), list(range(10))])
assert not mi4.is_(mi3)
mi5 = mi.view()
with tm.assert_produces_warning(FutureWarning):
mi5.set_levels(mi5.levels, inplace=True)
mi5 = mi5.set_levels(mi5.levels)
assert not mi5.is_(mi)


Expand Down
Loading

0 comments on commit 80bdd41

Please sign in to comment.