Skip to content

Commit 092ab6e

Browse files
jbrockmendelproost
authored andcommitted
DEPR: enforce nested-renaming deprecation (pandas-dev#29608)
1 parent 8266c17 commit 092ab6e

File tree

10 files changed

+101
-222
lines changed

10 files changed

+101
-222
lines changed

doc/source/whatsnew/v1.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more.
279279
- Removed the previously deprecated ``reduce`` and ``broadcast`` arguments from :meth:`DataFrame.apply` (:issue:`18577`)
280280
- Removed the previously deprecated ``assert_raises_regex`` function in ``pandas.util.testing`` (:issue:`29174`)
281281
- Removed :meth:`Index.is_lexsorted_for_tuple` (:issue:`29305`)
282+
- Removed support for nexted renaming in :meth:`DataFrame.aggregate`, :meth:`Series.aggregate`, :meth:`DataFrameGroupBy.aggregate`, :meth:`SeriesGroupBy.aggregate`, :meth:`Rolling.aggregate` (:issue:`29608`)
282283
-
283284

284285
.. _whatsnew_1000.performance:

pandas/core/base.py

+8-36
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,7 @@ def _try_aggregate_string_function(self, arg: str, *args, **kwargs):
283283
# people may try to aggregate on a non-callable attribute
284284
# but don't let them think they can pass args to it
285285
assert len(args) == 0
286-
assert (
287-
len([kwarg for kwarg in kwargs if kwarg not in ["axis", "_level"]]) == 0
288-
)
286+
assert len([kwarg for kwarg in kwargs if kwarg not in ["axis"]]) == 0
289287
return f
290288

291289
f = getattr(np, arg, None)
@@ -324,34 +322,17 @@ def _aggregate(self, arg, *args, **kwargs):
324322
_axis = kwargs.pop("_axis", None)
325323
if _axis is None:
326324
_axis = getattr(self, "axis", 0)
327-
_level = kwargs.pop("_level", None)
328325

329326
if isinstance(arg, str):
330327
return self._try_aggregate_string_function(arg, *args, **kwargs), None
331328

332329
if isinstance(arg, dict):
333-
334330
# aggregate based on the passed dict
335331
if _axis != 0: # pragma: no cover
336332
raise ValueError("Can only pass dict with axis=0")
337333

338334
obj = self._selected_obj
339335

340-
def nested_renaming_depr(level: int = 4):
341-
# deprecation of nested renaming
342-
# GH 15931
343-
msg = textwrap.dedent(
344-
"""\
345-
using a dict with renaming is deprecated and will be removed
346-
in a future version.
347-
348-
For column-specific groupby renaming, use named aggregation
349-
350-
>>> df.groupby(...).agg(name=('column', aggfunc))
351-
"""
352-
)
353-
warnings.warn(msg, FutureWarning, stacklevel=level)
354-
355336
# if we have a dict of any non-scalars
356337
# eg. {'A' : ['mean']}, normalize all to
357338
# be list-likes
@@ -374,18 +355,9 @@ def nested_renaming_depr(level: int = 4):
374355
# not ok
375356
# {'ra' : { 'A' : 'mean' }}
376357
if isinstance(v, dict):
377-
is_nested_renamer = True
378-
379-
if k not in obj.columns:
380-
msg = (
381-
"cannot perform renaming for {key} with a "
382-
"nested dictionary"
383-
).format(key=k)
384-
raise SpecificationError(msg)
385-
nested_renaming_depr(4 + (_level or 0))
386-
358+
raise SpecificationError("nested renamer is not supported")
387359
elif isinstance(obj, ABCSeries):
388-
nested_renaming_depr()
360+
raise SpecificationError("nested renamer is not supported")
389361
elif isinstance(obj, ABCDataFrame) and k not in obj.columns:
390362
raise KeyError("Column '{col}' does not exist!".format(col=k))
391363

@@ -398,7 +370,7 @@ def nested_renaming_depr(level: int = 4):
398370
if isinstance(obj, ABCDataFrame) and len(
399371
obj.columns.intersection(keys)
400372
) != len(keys):
401-
nested_renaming_depr()
373+
raise SpecificationError("nested renamer is not supported")
402374

403375
from pandas.core.reshape.concat import concat
404376

@@ -411,14 +383,14 @@ def _agg_1dim(name, how, subset=None):
411383
raise SpecificationError(
412384
"nested dictionary is ambiguous in aggregation"
413385
)
414-
return colg.aggregate(how, _level=(_level or 0) + 1)
386+
return colg.aggregate(how)
415387

416388
def _agg_2dim(name, how):
417389
"""
418390
aggregate a 2-dim with how
419391
"""
420392
colg = self._gotitem(self._selection, ndim=2, subset=obj)
421-
return colg.aggregate(how, _level=None)
393+
return colg.aggregate(how)
422394

423395
def _agg(arg, func):
424396
"""
@@ -535,7 +507,7 @@ def is_any_frame() -> bool:
535507
return result, True
536508
elif is_list_like(arg):
537509
# we require a list, but not an 'str'
538-
return self._aggregate_multiple_funcs(arg, _level=_level, _axis=_axis), None
510+
return self._aggregate_multiple_funcs(arg, _axis=_axis), None
539511
else:
540512
result = None
541513

@@ -546,7 +518,7 @@ def is_any_frame() -> bool:
546518
# caller can react
547519
return result, True
548520

549-
def _aggregate_multiple_funcs(self, arg, _level, _axis):
521+
def _aggregate_multiple_funcs(self, arg, _axis):
550522
from pandas.core.reshape.concat import concat
551523

552524
if _axis != 0:

pandas/core/groupby/generic.py

+8-24
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from textwrap import dedent
1212
import typing
1313
from typing import Any, Callable, FrozenSet, Iterable, Sequence, Type, Union, cast
14-
import warnings
1514

1615
import numpy as np
1716

@@ -213,7 +212,6 @@ def apply(self, func, *args, **kwargs):
213212
)
214213
@Appender(_shared_docs["aggregate"])
215214
def aggregate(self, func=None, *args, **kwargs):
216-
_level = kwargs.pop("_level", None)
217215

218216
relabeling = func is None
219217
columns = None
@@ -232,7 +230,7 @@ def aggregate(self, func=None, *args, **kwargs):
232230
# Catch instances of lists / tuples
233231
# but not the class list / tuple itself.
234232
func = _maybe_mangle_lambdas(func)
235-
ret = self._aggregate_multiple_funcs(func, (_level or 0) + 1)
233+
ret = self._aggregate_multiple_funcs(func)
236234
if relabeling:
237235
ret.columns = columns
238236
else:
@@ -256,32 +254,22 @@ def aggregate(self, func=None, *args, **kwargs):
256254
if not self.as_index: # pragma: no cover
257255
print("Warning, ignoring as_index=True")
258256

259-
# _level handled at higher
260-
if not _level and isinstance(ret, dict):
257+
if isinstance(ret, dict):
261258
from pandas import concat
262259

263260
ret = concat(ret, axis=1)
264261
return ret
265262

266263
agg = aggregate
267264

268-
def _aggregate_multiple_funcs(self, arg, _level):
265+
def _aggregate_multiple_funcs(self, arg):
269266
if isinstance(arg, dict):
270267

271268
# show the deprecation, but only if we
272269
# have not shown a higher level one
273270
# GH 15931
274-
if isinstance(self._selected_obj, Series) and _level <= 1:
275-
msg = dedent(
276-
"""\
277-
using a dict on a Series for aggregation
278-
is deprecated and will be removed in a future version. Use \
279-
named aggregation instead.
280-
281-
>>> grouper.agg(name_1=func_1, name_2=func_2)
282-
"""
283-
)
284-
warnings.warn(msg, FutureWarning, stacklevel=3)
271+
if isinstance(self._selected_obj, Series):
272+
raise SpecificationError("nested renamer is not supported")
285273

286274
columns = list(arg.keys())
287275
arg = arg.items()
@@ -317,8 +305,7 @@ def _aggregate_multiple_funcs(self, arg, _level):
317305

318306
if any(isinstance(x, DataFrame) for x in results.values()):
319307
# let higher level handle
320-
if _level:
321-
return results
308+
return results
322309

323310
return DataFrame(results, columns=columns)
324311

@@ -845,7 +832,6 @@ class DataFrameGroupBy(GroupBy):
845832
)
846833
@Appender(_shared_docs["aggregate"])
847834
def aggregate(self, func=None, *args, **kwargs):
848-
_level = kwargs.pop("_level", None)
849835

850836
relabeling = func is None and _is_multi_agg_with_relabel(**kwargs)
851837
if relabeling:
@@ -858,7 +844,7 @@ def aggregate(self, func=None, *args, **kwargs):
858844

859845
func = _maybe_mangle_lambdas(func)
860846

861-
result, how = self._aggregate(func, _level=_level, *args, **kwargs)
847+
result, how = self._aggregate(func, *args, **kwargs)
862848
if how is None:
863849
return result
864850

@@ -878,9 +864,7 @@ def aggregate(self, func=None, *args, **kwargs):
878864

879865
# try to treat as if we are passing a list
880866
try:
881-
result = self._aggregate_multiple_funcs(
882-
[func], _level=_level, _axis=self.axis
883-
)
867+
result = self._aggregate_multiple_funcs([func], _axis=self.axis)
884868
except ValueError as err:
885869
if "no results" not in str(err):
886870
# raised directly by _aggregate_multiple_funcs

pandas/tests/frame/test_apply.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from pandas import DataFrame, MultiIndex, Series, Timestamp, date_range, notna
1414
from pandas.conftest import _get_cython_table_params
1515
from pandas.core.apply import frame_apply
16+
from pandas.core.base import SpecificationError
1617
import pandas.util.testing as tm
1718

1819

@@ -1094,7 +1095,8 @@ def test_agg_dict_nested_renaming_depr(self):
10941095
df = pd.DataFrame({"A": range(5), "B": 5})
10951096

10961097
# nested renaming
1097-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
1098+
msg = r"nested renamer is not supported"
1099+
with pytest.raises(SpecificationError, match=msg):
10981100
df.agg({"A": {"foo": "min"}, "B": {"bar": "max"}})
10991101

11001102
def test_agg_reduce(self, axis, float_frame):

pandas/tests/groupby/aggregate/test_aggregate.py

+12-13
Original file line numberDiff line numberDiff line change
@@ -267,16 +267,16 @@ def bar(x):
267267
return np.std(x, ddof=1)
268268

269269
# this uses column selection & renaming
270-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
270+
msg = r"nested renamer is not supported"
271+
with pytest.raises(SpecificationError, match=msg):
271272
d = OrderedDict(
272273
[["C", np.mean], ["D", OrderedDict([["foo", np.mean], ["bar", np.std]])]]
273274
)
274-
result = grouped.aggregate(d)
275+
grouped.aggregate(d)
275276

277+
# But without renaming, these functions are OK
276278
d = OrderedDict([["C", [np.mean]], ["D", [foo, bar]]])
277-
expected = grouped.aggregate(d)
278-
279-
tm.assert_frame_equal(result, expected)
279+
grouped.aggregate(d)
280280

281281

282282
def test_multi_function_flexible_mix(df):
@@ -288,26 +288,25 @@ def test_multi_function_flexible_mix(df):
288288
[["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", {"sum": "sum"}]]
289289
)
290290
# this uses column selection & renaming
291-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
292-
expected = grouped.aggregate(d)
291+
msg = r"nested renamer is not supported"
292+
with pytest.raises(SpecificationError, match=msg):
293+
grouped.aggregate(d)
293294

294295
# Test 1
295296
d = OrderedDict(
296297
[["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", "sum"]]
297298
)
298299
# this uses column selection & renaming
299-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
300-
result = grouped.aggregate(d)
301-
tm.assert_frame_equal(result, expected)
300+
with pytest.raises(SpecificationError, match=msg):
301+
grouped.aggregate(d)
302302

303303
# Test 2
304304
d = OrderedDict(
305305
[["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", ["sum"]]]
306306
)
307307
# this uses column selection & renaming
308-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
309-
result = grouped.aggregate(d)
310-
tm.assert_frame_equal(result, expected)
308+
with pytest.raises(SpecificationError, match=msg):
309+
grouped.aggregate(d)
311310

312311

313312
def test_groupby_agg_coercing_bools():

pandas/tests/groupby/aggregate/test_other.py

+19-39
Original file line numberDiff line numberDiff line change
@@ -211,31 +211,26 @@ def test_aggregate_api_consistency():
211211
expected = pd.concat([c_mean, c_sum, d_mean, d_sum], axis=1)
212212
expected.columns = MultiIndex.from_product([["C", "D"], ["mean", "sum"]])
213213

214-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
215-
result = grouped[["D", "C"]].agg({"r": np.sum, "r2": np.mean})
216-
expected = pd.concat([d_sum, c_sum, d_mean, c_mean], axis=1)
217-
expected.columns = MultiIndex.from_product([["r", "r2"], ["D", "C"]])
218-
tm.assert_frame_equal(result, expected, check_like=True)
214+
msg = r"nested renamer is not supported"
215+
with pytest.raises(SpecificationError, match=msg):
216+
grouped[["D", "C"]].agg({"r": np.sum, "r2": np.mean})
219217

220218

221219
def test_agg_dict_renaming_deprecation():
222220
# 15931
223221
df = pd.DataFrame({"A": [1, 1, 1, 2, 2], "B": range(5), "C": range(5)})
224222

225-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False) as w:
223+
msg = r"nested renamer is not supported"
224+
with pytest.raises(SpecificationError, match=msg):
226225
df.groupby("A").agg(
227226
{"B": {"foo": ["sum", "max"]}, "C": {"bar": ["count", "min"]}}
228227
)
229-
assert "using a dict with renaming" in str(w[0].message)
230-
assert "named aggregation" in str(w[0].message)
231228

232-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
229+
with pytest.raises(SpecificationError, match=msg):
233230
df.groupby("A")[["B", "C"]].agg({"ma": "max"})
234231

235-
with tm.assert_produces_warning(FutureWarning) as w:
232+
with pytest.raises(SpecificationError, match=msg):
236233
df.groupby("A").B.agg({"foo": "count"})
237-
assert "using a dict on a Series for aggregation" in str(w[0].message)
238-
assert "named aggregation instead." in str(w[0].message)
239234

240235

241236
def test_agg_compat():
@@ -251,18 +246,12 @@ def test_agg_compat():
251246

252247
g = df.groupby(["A", "B"])
253248

254-
expected = pd.concat([g["D"].sum(), g["D"].std()], axis=1)
255-
expected.columns = MultiIndex.from_tuples([("C", "sum"), ("C", "std")])
256-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
257-
result = g["D"].agg({"C": ["sum", "std"]})
258-
tm.assert_frame_equal(result, expected, check_like=True)
259-
260-
expected = pd.concat([g["D"].sum(), g["D"].std()], axis=1)
261-
expected.columns = ["C", "D"]
249+
msg = r"nested renamer is not supported"
250+
with pytest.raises(SpecificationError, match=msg):
251+
g["D"].agg({"C": ["sum", "std"]})
262252

263-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
264-
result = g["D"].agg({"C": "sum", "D": "std"})
265-
tm.assert_frame_equal(result, expected, check_like=True)
253+
with pytest.raises(SpecificationError, match=msg):
254+
g["D"].agg({"C": "sum", "D": "std"})
266255

267256

268257
def test_agg_nested_dicts():
@@ -278,29 +267,20 @@ def test_agg_nested_dicts():
278267

279268
g = df.groupby(["A", "B"])
280269

281-
msg = r"cannot perform renaming for r[1-2] with a nested dictionary"
270+
msg = r"nested renamer is not supported"
282271
with pytest.raises(SpecificationError, match=msg):
283272
g.aggregate({"r1": {"C": ["mean", "sum"]}, "r2": {"D": ["mean", "sum"]}})
284273

285-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
286-
result = g.agg({"C": {"ra": ["mean", "std"]}, "D": {"rb": ["mean", "std"]}})
287-
expected = pd.concat(
288-
[g["C"].mean(), g["C"].std(), g["D"].mean(), g["D"].std()], axis=1
289-
)
290-
expected.columns = pd.MultiIndex.from_tuples(
291-
[("ra", "mean"), ("ra", "std"), ("rb", "mean"), ("rb", "std")]
292-
)
293-
tm.assert_frame_equal(result, expected, check_like=True)
274+
with pytest.raises(SpecificationError, match=msg):
275+
g.agg({"C": {"ra": ["mean", "std"]}, "D": {"rb": ["mean", "std"]}})
294276

295277
# same name as the original column
296278
# GH9052
297-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
298-
expected = g["D"].agg({"result1": np.sum, "result2": np.mean})
299-
expected = expected.rename(columns={"result1": "D"})
279+
with pytest.raises(SpecificationError, match=msg):
280+
g["D"].agg({"result1": np.sum, "result2": np.mean})
300281

301-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
302-
result = g["D"].agg({"D": np.sum, "result2": np.mean})
303-
tm.assert_frame_equal(result, expected, check_like=True)
282+
with pytest.raises(SpecificationError, match=msg):
283+
g["D"].agg({"D": np.sum, "result2": np.mean})
304284

305285

306286
def test_agg_item_by_item_raise_typeerror():

0 commit comments

Comments
 (0)