Skip to content

Commit 5dee73b

Browse files
authored
DEPR: Deprecate inplace param in MultiIndex.set_codes and MultiIndex.set_levels (#35626)
1 parent 6875a05 commit 5dee73b

File tree

13 files changed

+94
-37
lines changed

13 files changed

+94
-37
lines changed

doc/source/user_guide/indexing.rst

+2-6
Original file line numberDiff line numberDiff line change
@@ -1532,12 +1532,8 @@ Setting metadata
15321532
~~~~~~~~~~~~~~~~
15331533

15341534
Indexes are "mostly immutable", but it is possible to set and change their
1535-
metadata, like the index ``name`` (or, for ``MultiIndex``, ``levels`` and
1536-
``codes``).
1537-
1538-
You can use the ``rename``, ``set_names``, ``set_levels``, and ``set_codes``
1539-
to set these attributes directly. They default to returning a copy; however,
1540-
you can specify ``inplace=True`` to have the data change in place.
1535+
``name`` attribute. You can use the ``rename``, ``set_names`` to set these attributes
1536+
directly, and they default to returning a copy.
15411537

15421538
See :ref:`Advanced Indexing <advanced>` for usage of MultiIndexes.
15431539

doc/source/whatsnew/v1.2.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Other enhancements
4747

4848
Deprecations
4949
~~~~~~~~~~~~
50-
50+
- Deprecated parameter ``inplace`` in :meth:`MultiIndex.set_codes` and :meth:`MultiIndex.set_levels` (:issue:`35626`)
5151
-
5252
-
5353

pandas/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ def multiindex_year_month_day_dataframe_random_data():
359359
tdf = tm.makeTimeDataFrame(100)
360360
ymd = tdf.groupby([lambda x: x.year, lambda x: x.month, lambda x: x.day]).sum()
361361
# use Int64Index, to make sure things work
362-
ymd.index.set_levels([lev.astype("i8") for lev in ymd.index.levels], inplace=True)
362+
ymd.index = ymd.index.set_levels([lev.astype("i8") for lev in ymd.index.levels])
363363
ymd.index.set_names(["year", "month", "day"], inplace=True)
364364
return ymd
365365

pandas/core/indexes/multi.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ def _set_levels(
740740
self._tuples = None
741741
self._reset_cache()
742742

743-
def set_levels(self, levels, level=None, inplace=False, verify_integrity=True):
743+
def set_levels(self, levels, level=None, inplace=None, verify_integrity=True):
744744
"""
745745
Set new levels on MultiIndex. Defaults to returning new index.
746746
@@ -752,6 +752,8 @@ def set_levels(self, levels, level=None, inplace=False, verify_integrity=True):
752752
Level(s) to set (None for all levels).
753753
inplace : bool
754754
If True, mutates in place.
755+
756+
.. deprecated:: 1.2.0
755757
verify_integrity : bool, default True
756758
If True, checks that levels and codes are compatible.
757759
@@ -822,6 +824,15 @@ def set_levels(self, levels, level=None, inplace=False, verify_integrity=True):
822824
>>> idx.set_levels([['a', 'b', 'c'], [1, 2, 3, 4]], level=[0, 1]).levels
823825
FrozenList([['a', 'b', 'c'], [1, 2, 3, 4]])
824826
"""
827+
if inplace is not None:
828+
warnings.warn(
829+
"inplace is deprecated and will be removed in a future version.",
830+
FutureWarning,
831+
stacklevel=2,
832+
)
833+
else:
834+
inplace = False
835+
825836
if is_list_like(levels) and not isinstance(levels, Index):
826837
levels = list(levels)
827838

@@ -898,7 +909,7 @@ def _set_codes(
898909
self._tuples = None
899910
self._reset_cache()
900911

901-
def set_codes(self, codes, level=None, inplace=False, verify_integrity=True):
912+
def set_codes(self, codes, level=None, inplace=None, verify_integrity=True):
902913
"""
903914
Set new codes on MultiIndex. Defaults to returning new index.
904915
@@ -914,6 +925,8 @@ def set_codes(self, codes, level=None, inplace=False, verify_integrity=True):
914925
Level(s) to set (None for all levels).
915926
inplace : bool
916927
If True, mutates in place.
928+
929+
.. deprecated:: 1.2.0
917930
verify_integrity : bool (default True)
918931
If True, checks that levels and codes are compatible.
919932
@@ -958,6 +971,15 @@ def set_codes(self, codes, level=None, inplace=False, verify_integrity=True):
958971
(1, 'two')],
959972
names=['foo', 'bar'])
960973
"""
974+
if inplace is not None:
975+
warnings.warn(
976+
"inplace is deprecated and will be removed in a future version.",
977+
FutureWarning,
978+
stacklevel=2,
979+
)
980+
else:
981+
inplace = False
982+
961983
if level is not None and not is_list_like(level):
962984
if not is_list_like(codes):
963985
raise TypeError("Codes must be list-like")

pandas/tests/frame/methods/test_sort_index.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,8 @@ def test_sort_index_and_reconstruction(self):
555555
),
556556
)
557557

558-
df.columns.set_levels(
559-
pd.to_datetime(df.columns.levels[1]), level=1, inplace=True
558+
df.columns = df.columns.set_levels(
559+
pd.to_datetime(df.columns.levels[1]), level=1
560560
)
561561
assert not df.columns.is_lexsorted()
562562
assert not df.columns.is_monotonic

pandas/tests/indexes/multi/test_compat.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def test_inplace_mutation_resets_values():
8484
tm.assert_almost_equal(mi1.values, vals)
8585

8686
# Inplace should kill _tuples
87-
mi1.set_levels(levels2, inplace=True)
87+
with tm.assert_produces_warning(FutureWarning):
88+
mi1.set_levels(levels2, inplace=True)
8889
tm.assert_almost_equal(mi1.values, vals2)
8990

9091
# Make sure label setting works too
@@ -103,7 +104,8 @@ def test_inplace_mutation_resets_values():
103104
tm.assert_almost_equal(exp_values, new_values)
104105

105106
# ...and again setting inplace should kill _tuples, etc
106-
mi2.set_codes(codes2, inplace=True)
107+
with tm.assert_produces_warning(FutureWarning):
108+
mi2.set_codes(codes2, inplace=True)
107109
tm.assert_almost_equal(mi2.values, new_values)
108110

109111

pandas/tests/indexes/multi/test_duplicates.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def test_duplicate_multiindex_codes():
9191
mi = MultiIndex.from_arrays([["A", "A", "B", "B", "B"], [1, 2, 1, 2, 3]])
9292
msg = r"Level values must be unique: \[[AB', ]+\] on level 0"
9393
with pytest.raises(ValueError, match=msg):
94-
mi.set_levels([["A", "B", "A", "A", "B"], [2, 1, 3, -2, 5]], inplace=True)
94+
with tm.assert_produces_warning(FutureWarning):
95+
mi.set_levels([["A", "B", "A", "A", "B"], [2, 1, 3, -2, 5]], inplace=True)
9596

9697

9798
@pytest.mark.parametrize("names", [["a", "b", "a"], [1, 1, 2], [1, "a", 1]])

pandas/tests/indexes/multi/test_equivalence.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,12 @@ def test_is_():
192192
mi4 = mi3.view()
193193

194194
# GH 17464 - Remove duplicate MultiIndex levels
195-
mi4.set_levels([list(range(10)), list(range(10))], inplace=True)
195+
with tm.assert_produces_warning(FutureWarning):
196+
mi4.set_levels([list(range(10)), list(range(10))], inplace=True)
196197
assert not mi4.is_(mi3)
197198
mi5 = mi.view()
198-
mi5.set_levels(mi5.levels, inplace=True)
199+
with tm.assert_produces_warning(FutureWarning):
200+
mi5.set_levels(mi5.levels, inplace=True)
199201
assert not mi5.is_(mi)
200202

201203

pandas/tests/indexes/multi/test_get_set.py

+42-13
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ def test_set_levels(idx):
9393

9494
# level changing [w/ mutation]
9595
ind2 = idx.copy()
96-
inplace_return = ind2.set_levels(new_levels, inplace=True)
96+
with tm.assert_produces_warning(FutureWarning):
97+
inplace_return = ind2.set_levels(new_levels, inplace=True)
9798
assert inplace_return is None
9899
assert_matching(ind2.levels, new_levels)
99100

@@ -113,20 +114,23 @@ def test_set_levels(idx):
113114

114115
# level changing specific level [w/ mutation]
115116
ind2 = idx.copy()
116-
inplace_return = ind2.set_levels(new_levels[0], level=0, inplace=True)
117+
with tm.assert_produces_warning(FutureWarning):
118+
inplace_return = ind2.set_levels(new_levels[0], level=0, inplace=True)
117119
assert inplace_return is None
118120
assert_matching(ind2.levels, [new_levels[0], levels[1]])
119121
assert_matching(idx.levels, levels)
120122

121123
ind2 = idx.copy()
122-
inplace_return = ind2.set_levels(new_levels[1], level=1, inplace=True)
124+
with tm.assert_produces_warning(FutureWarning):
125+
inplace_return = ind2.set_levels(new_levels[1], level=1, inplace=True)
123126
assert inplace_return is None
124127
assert_matching(ind2.levels, [levels[0], new_levels[1]])
125128
assert_matching(idx.levels, levels)
126129

127130
# level changing multiple levels [w/ mutation]
128131
ind2 = idx.copy()
129-
inplace_return = ind2.set_levels(new_levels, level=[0, 1], inplace=True)
132+
with tm.assert_produces_warning(FutureWarning):
133+
inplace_return = ind2.set_levels(new_levels, level=[0, 1], inplace=True)
130134
assert inplace_return is None
131135
assert_matching(ind2.levels, new_levels)
132136
assert_matching(idx.levels, levels)
@@ -136,19 +140,23 @@ def test_set_levels(idx):
136140
original_index = idx.copy()
137141
for inplace in [True, False]:
138142
with pytest.raises(ValueError, match="^On"):
139-
idx.set_levels(["c"], level=0, inplace=inplace)
143+
with tm.assert_produces_warning(FutureWarning):
144+
idx.set_levels(["c"], level=0, inplace=inplace)
140145
assert_matching(idx.levels, original_index.levels, check_dtype=True)
141146

142147
with pytest.raises(ValueError, match="^On"):
143-
idx.set_codes([0, 1, 2, 3, 4, 5], level=0, inplace=inplace)
148+
with tm.assert_produces_warning(FutureWarning):
149+
idx.set_codes([0, 1, 2, 3, 4, 5], level=0, inplace=inplace)
144150
assert_matching(idx.codes, original_index.codes, check_dtype=True)
145151

146152
with pytest.raises(TypeError, match="^Levels"):
147-
idx.set_levels("c", level=0, inplace=inplace)
153+
with tm.assert_produces_warning(FutureWarning):
154+
idx.set_levels("c", level=0, inplace=inplace)
148155
assert_matching(idx.levels, original_index.levels, check_dtype=True)
149156

150157
with pytest.raises(TypeError, match="^Codes"):
151-
idx.set_codes(1, level=0, inplace=inplace)
158+
with tm.assert_produces_warning(FutureWarning):
159+
idx.set_codes(1, level=0, inplace=inplace)
152160
assert_matching(idx.codes, original_index.codes, check_dtype=True)
153161

154162

@@ -168,7 +176,8 @@ def test_set_codes(idx):
168176

169177
# changing label w/ mutation
170178
ind2 = idx.copy()
171-
inplace_return = ind2.set_codes(new_codes, inplace=True)
179+
with tm.assert_produces_warning(FutureWarning):
180+
inplace_return = ind2.set_codes(new_codes, inplace=True)
172181
assert inplace_return is None
173182
assert_matching(ind2.codes, new_codes)
174183

@@ -188,20 +197,23 @@ def test_set_codes(idx):
188197

189198
# label changing specific level w/ mutation
190199
ind2 = idx.copy()
191-
inplace_return = ind2.set_codes(new_codes[0], level=0, inplace=True)
200+
with tm.assert_produces_warning(FutureWarning):
201+
inplace_return = ind2.set_codes(new_codes[0], level=0, inplace=True)
192202
assert inplace_return is None
193203
assert_matching(ind2.codes, [new_codes[0], codes[1]])
194204
assert_matching(idx.codes, codes)
195205

196206
ind2 = idx.copy()
197-
inplace_return = ind2.set_codes(new_codes[1], level=1, inplace=True)
207+
with tm.assert_produces_warning(FutureWarning):
208+
inplace_return = ind2.set_codes(new_codes[1], level=1, inplace=True)
198209
assert inplace_return is None
199210
assert_matching(ind2.codes, [codes[0], new_codes[1]])
200211
assert_matching(idx.codes, codes)
201212

202213
# codes changing multiple levels [w/ mutation]
203214
ind2 = idx.copy()
204-
inplace_return = ind2.set_codes(new_codes, level=[0, 1], inplace=True)
215+
with tm.assert_produces_warning(FutureWarning):
216+
inplace_return = ind2.set_codes(new_codes, level=[0, 1], inplace=True)
205217
assert inplace_return is None
206218
assert_matching(ind2.codes, new_codes)
207219
assert_matching(idx.codes, codes)
@@ -217,7 +229,8 @@ def test_set_codes(idx):
217229

218230
# [w/ mutation]
219231
result = ind.copy()
220-
result.set_codes(codes=new_codes, level=1, inplace=True)
232+
with tm.assert_produces_warning(FutureWarning):
233+
result.set_codes(codes=new_codes, level=1, inplace=True)
221234
assert result.equals(expected)
222235

223236

@@ -329,3 +342,19 @@ def test_set_levels_with_iterable():
329342
[expected_sizes, colors], names=["size", "color"]
330343
)
331344
tm.assert_index_equal(result, expected)
345+
346+
347+
@pytest.mark.parametrize("inplace", [True, False])
348+
def test_set_codes_inplace_deprecated(idx, inplace):
349+
new_codes = idx.codes[1][::-1]
350+
351+
with tm.assert_produces_warning(FutureWarning):
352+
idx.set_codes(codes=new_codes, level=1, inplace=inplace)
353+
354+
355+
@pytest.mark.parametrize("inplace", [True, False])
356+
def test_set_levels_inplace_deprecated(idx, inplace):
357+
new_level = idx.levels[1].copy()
358+
359+
with tm.assert_produces_warning(FutureWarning):
360+
idx.set_levels(levels=new_level, level=1, inplace=inplace)

pandas/tests/indexes/multi/test_integrity.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ def test_metadata_immutable(idx):
220220
def test_level_setting_resets_attributes():
221221
ind = pd.MultiIndex.from_arrays([["A", "A", "B", "B", "B"], [1, 2, 1, 2, 3]])
222222
assert ind.is_monotonic
223-
ind.set_levels([["A", "B"], [1, 3, 2]], inplace=True)
223+
with tm.assert_produces_warning(FutureWarning):
224+
ind.set_levels([["A", "B"], [1, 3, 2]], inplace=True)
224225
# if this fails, probably didn't reset the cache correctly.
225226
assert not ind.is_monotonic
226227

pandas/tests/indexing/multiindex/test_sorted.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ def test_frame_getitem_not_sorted2(self, key):
4343
df2 = df.set_index(["col1", "col2"])
4444
df2_original = df2.copy()
4545

46-
return_value = df2.index.set_levels(["b", "d", "a"], level="col1", inplace=True)
46+
with tm.assert_produces_warning(FutureWarning):
47+
return_value = df2.index.set_levels(
48+
["b", "d", "a"], level="col1", inplace=True
49+
)
4750
assert return_value is None
48-
return_value = df2.index.set_codes([0, 1, 0, 2], level="col1", inplace=True)
51+
with tm.assert_produces_warning(FutureWarning):
52+
return_value = df2.index.set_codes([0, 1, 0, 2], level="col1", inplace=True)
4953
assert return_value is None
5054
assert not df2.index.is_lexsorted()
5155
assert not df2.index.is_monotonic

pandas/tests/reshape/test_melt.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ def test_invalid_separator(self):
799799
expected = expected.set_index(["id", "year"])[
800800
["X", "A2010", "A2011", "B2010", "A", "B"]
801801
]
802-
expected.index.set_levels([0, 1], level=0, inplace=True)
802+
expected.index = expected.index.set_levels([0, 1], level=0)
803803
result = wide_to_long(df, ["A", "B"], i="id", j="year", sep=sep)
804804
tm.assert_frame_equal(result.sort_index(axis=1), expected.sort_index(axis=1))
805805

@@ -861,7 +861,7 @@ def test_invalid_suffixtype(self):
861861
expected = pd.DataFrame(exp_data).astype({"year": "int"})
862862

863863
expected = expected.set_index(["id", "year"])
864-
expected.index.set_levels([0, 1], level=0, inplace=True)
864+
expected.index = expected.index.set_levels([0, 1], level=0)
865865
result = wide_to_long(df, ["A", "B"], i="id", j="year")
866866
tm.assert_frame_equal(result.sort_index(axis=1), expected.sort_index(axis=1))
867867

pandas/tests/test_multilevel.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def setup_method(self, method):
6363
).sum()
6464

6565
# use Int64Index, to make sure things work
66-
self.ymd.index.set_levels(
67-
[lev.astype("i8") for lev in self.ymd.index.levels], inplace=True
66+
self.ymd.index = self.ymd.index.set_levels(
67+
[lev.astype("i8") for lev in self.ymd.index.levels]
6868
)
6969
self.ymd.index.set_names(["year", "month", "day"], inplace=True)
7070

0 commit comments

Comments
 (0)