From 658584a32007d07a9f11a66512e3723c1374dde1 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 13 Nov 2019 19:02:56 -0800 Subject: [PATCH 1/4] DEPR: enforce nested-renaming deprecation --- pandas/core/base.py | 45 +++---------- pandas/core/groupby/generic.py | 32 +++------ pandas/tests/frame/test_apply.py | 4 +- .../tests/groupby/aggregate/test_aggregate.py | 25 ++++--- pandas/tests/groupby/aggregate/test_other.py | 58 ++++++---------- pandas/tests/groupby/test_groupby.py | 20 +++--- pandas/tests/resample/test_resample_api.py | 66 ++++++------------- pandas/tests/series/test_apply.py | 30 +++------ pandas/tests/window/test_api.py | 43 +++--------- 9 files changed, 100 insertions(+), 223 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index e070005c56d7a..a7b866ae492ec 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -283,9 +283,7 @@ def _try_aggregate_string_function(self, arg: str, *args, **kwargs): # people may try to aggregate on a non-callable attribute # but don't let them think they can pass args to it assert len(args) == 0 - assert ( - len([kwarg for kwarg in kwargs if kwarg not in ["axis", "_level"]]) == 0 - ) + assert len([kwarg for kwarg in kwargs if kwarg not in ["axis"]]) == 0 return f f = getattr(np, arg, None) @@ -324,7 +322,6 @@ def _aggregate(self, arg, *args, **kwargs): _axis = kwargs.pop("_axis", None) if _axis is None: _axis = getattr(self, "axis", 0) - _level = kwargs.pop("_level", None) if isinstance(arg, str): return self._try_aggregate_string_function(arg, *args, **kwargs), None @@ -337,21 +334,6 @@ def _aggregate(self, arg, *args, **kwargs): obj = self._selected_obj - def nested_renaming_depr(level: int = 4): - # deprecation of nested renaming - # GH 15931 - msg = textwrap.dedent( - """\ - using a dict with renaming is deprecated and will be removed - in a future version. - - For column-specific groupby renaming, use named aggregation - - >>> df.groupby(...).agg(name=('column', aggfunc)) - """ - ) - warnings.warn(msg, FutureWarning, stacklevel=level) - # if we have a dict of any non-scalars # eg. {'A' : ['mean']}, normalize all to # be list-likes @@ -373,19 +355,8 @@ def nested_renaming_depr(level: int = 4): # not ok # {'ra' : { 'A' : 'mean' }} - if isinstance(v, dict): - is_nested_renamer = True - - if k not in obj.columns: - msg = ( - "cannot perform renaming for {key} with a " - "nested dictionary" - ).format(key=k) - raise SpecificationError(msg) - nested_renaming_depr(4 + (_level or 0)) - - elif isinstance(obj, ABCSeries): - nested_renaming_depr() + if isinstance(v, (dict, ABCSeries)): + raise SpecificationError("nested renamer is not supported") elif isinstance(obj, ABCDataFrame) and k not in obj.columns: raise KeyError("Column '{col}' does not exist!".format(col=k)) @@ -398,7 +369,7 @@ def nested_renaming_depr(level: int = 4): if isinstance(obj, ABCDataFrame) and len( obj.columns.intersection(keys) ) != len(keys): - nested_renaming_depr() + raise SpecificationError("nested renamer is not supported") from pandas.core.reshape.concat import concat @@ -411,14 +382,14 @@ def _agg_1dim(name, how, subset=None): raise SpecificationError( "nested dictionary is ambiguous in aggregation" ) - return colg.aggregate(how, _level=(_level or 0) + 1) + return colg.aggregate(how) def _agg_2dim(name, how): """ aggregate a 2-dim with how """ colg = self._gotitem(self._selection, ndim=2, subset=obj) - return colg.aggregate(how, _level=None) + return colg.aggregate(how) def _agg(arg, func): """ @@ -535,7 +506,7 @@ def is_any_frame() -> bool: return result, True elif is_list_like(arg): # we require a list, but not an 'str' - return self._aggregate_multiple_funcs(arg, _level=_level, _axis=_axis), None + return self._aggregate_multiple_funcs(arg, _axis=_axis), None else: result = None @@ -546,7 +517,7 @@ def is_any_frame() -> bool: # caller can react return result, True - def _aggregate_multiple_funcs(self, arg, _level, _axis): + def _aggregate_multiple_funcs(self, arg, _axis): from pandas.core.reshape.concat import concat if _axis != 0: diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 8f0b8a1e37af2..4894b2966ae9d 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -23,7 +23,6 @@ Union, cast, ) -import warnings import numpy as np @@ -225,7 +224,6 @@ def apply(self, func, *args, **kwargs): ) @Appender(_shared_docs["aggregate"]) def aggregate(self, func=None, *args, **kwargs): - _level = kwargs.pop("_level", None) relabeling = func is None columns = None @@ -244,7 +242,7 @@ def aggregate(self, func=None, *args, **kwargs): # Catch instances of lists / tuples # but not the class list / tuple itself. func = _maybe_mangle_lambdas(func) - ret = self._aggregate_multiple_funcs(func, (_level or 0) + 1) + ret = self._aggregate_multiple_funcs(func) if relabeling: ret.columns = columns else: @@ -271,8 +269,7 @@ def aggregate(self, func=None, *args, **kwargs): if not self.as_index: # pragma: no cover print("Warning, ignoring as_index=True") - # _level handled at higher - if not _level and isinstance(ret, dict): + if isinstance(ret, dict): from pandas import concat ret = concat(ret, axis=1) @@ -280,23 +277,14 @@ def aggregate(self, func=None, *args, **kwargs): agg = aggregate - def _aggregate_multiple_funcs(self, arg, _level): + def _aggregate_multiple_funcs(self, arg): if isinstance(arg, dict): # show the deprecation, but only if we # have not shown a higher level one # GH 15931 - if isinstance(self._selected_obj, Series) and _level <= 1: - msg = dedent( - """\ - using a dict on a Series for aggregation - is deprecated and will be removed in a future version. Use \ - named aggregation instead. - - >>> grouper.agg(name_1=func_1, name_2=func_2) - """ - ) - warnings.warn(msg, FutureWarning, stacklevel=3) + if isinstance(self._selected_obj, Series): + raise SpecificationError("nested renamer is not supported") columns = list(arg.keys()) arg = arg.items() @@ -332,8 +320,7 @@ def _aggregate_multiple_funcs(self, arg, _level): if any(isinstance(x, DataFrame) for x in results.values()): # let higher level handle - if _level: - return results + return results return DataFrame(results, columns=columns) @@ -859,7 +846,6 @@ class DataFrameGroupBy(GroupBy): ) @Appender(_shared_docs["aggregate"]) def aggregate(self, func=None, *args, **kwargs): - _level = kwargs.pop("_level", None) relabeling = func is None and _is_multi_agg_with_relabel(**kwargs) if relabeling: @@ -872,7 +858,7 @@ def aggregate(self, func=None, *args, **kwargs): func = _maybe_mangle_lambdas(func) - result, how = self._aggregate(func, _level=_level, *args, **kwargs) + result, how = self._aggregate(func, *args, **kwargs) if how is None: return result @@ -892,9 +878,7 @@ def aggregate(self, func=None, *args, **kwargs): # try to treat as if we are passing a list try: - result = self._aggregate_multiple_funcs( - [func], _level=_level, _axis=self.axis - ) + result = self._aggregate_multiple_funcs([func], _axis=self.axis) except ValueError as err: if "no results" not in str(err): # raised directly by _aggregate_multiple_funcs diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index fea50b3b7f75d..79b1af2437884 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -13,6 +13,7 @@ from pandas import DataFrame, MultiIndex, Series, Timestamp, date_range, notna from pandas.conftest import _get_cython_table_params from pandas.core.apply import frame_apply +from pandas.core.base import SpecificationError import pandas.util.testing as tm @@ -1094,7 +1095,8 @@ def test_agg_dict_nested_renaming_depr(self): df = pd.DataFrame({"A": range(5), "B": 5}) # nested renaming - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): df.agg({"A": {"foo": "min"}, "B": {"bar": "max"}}) def test_agg_reduce(self, axis, float_frame): diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 113c2c6d6d4ac..ea986058616d7 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -267,16 +267,16 @@ def bar(x): return np.std(x, ddof=1) # this uses column selection & renaming - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): d = OrderedDict( [["C", np.mean], ["D", OrderedDict([["foo", np.mean], ["bar", np.std]])]] ) - result = grouped.aggregate(d) + grouped.aggregate(d) + # But without renaming, these functions are OK d = OrderedDict([["C", [np.mean]], ["D", [foo, bar]]]) - expected = grouped.aggregate(d) - - tm.assert_frame_equal(result, expected) + grouped.aggregate(d) def test_multi_function_flexible_mix(df): @@ -288,26 +288,25 @@ def test_multi_function_flexible_mix(df): [["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", {"sum": "sum"}]] ) # this uses column selection & renaming - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - expected = grouped.aggregate(d) + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + grouped.aggregate(d) # Test 1 d = OrderedDict( [["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", "sum"]] ) # this uses column selection & renaming - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = grouped.aggregate(d) - tm.assert_frame_equal(result, expected) + with pytest.raises(SpecificationError, match=msg): + grouped.aggregate(d) # Test 2 d = OrderedDict( [["C", OrderedDict([["foo", "mean"], ["bar", "std"]])], ["D", ["sum"]]] ) # this uses column selection & renaming - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = grouped.aggregate(d) - tm.assert_frame_equal(result, expected) + with pytest.raises(SpecificationError, match=msg): + grouped.aggregate(d) def test_groupby_agg_coercing_bools(): diff --git a/pandas/tests/groupby/aggregate/test_other.py b/pandas/tests/groupby/aggregate/test_other.py index 1c297f3e2ada3..82a65726dca59 100644 --- a/pandas/tests/groupby/aggregate/test_other.py +++ b/pandas/tests/groupby/aggregate/test_other.py @@ -211,31 +211,26 @@ def test_aggregate_api_consistency(): expected = pd.concat([c_mean, c_sum, d_mean, d_sum], axis=1) expected.columns = MultiIndex.from_product([["C", "D"], ["mean", "sum"]]) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = grouped[["D", "C"]].agg({"r": np.sum, "r2": np.mean}) - expected = pd.concat([d_sum, c_sum, d_mean, c_mean], axis=1) - expected.columns = MultiIndex.from_product([["r", "r2"], ["D", "C"]]) - tm.assert_frame_equal(result, expected, check_like=True) + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + grouped[["D", "C"]].agg({"r": np.sum, "r2": np.mean}) def test_agg_dict_renaming_deprecation(): # 15931 df = pd.DataFrame({"A": [1, 1, 1, 2, 2], "B": range(5), "C": range(5)}) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False) as w: + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): df.groupby("A").agg( {"B": {"foo": ["sum", "max"]}, "C": {"bar": ["count", "min"]}} ) - assert "using a dict with renaming" in str(w[0].message) - assert "named aggregation" in str(w[0].message) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + with pytest.raises(SpecificationError, match=msg): df.groupby("A")[["B", "C"]].agg({"ma": "max"}) - with tm.assert_produces_warning(FutureWarning) as w: + with pytest.raises(SpecificationError, match=msg): df.groupby("A").B.agg({"foo": "count"}) - assert "using a dict on a Series for aggregation" in str(w[0].message) - assert "named aggregation instead." in str(w[0].message) def test_agg_compat(): @@ -251,18 +246,12 @@ def test_agg_compat(): g = df.groupby(["A", "B"]) - expected = pd.concat([g["D"].sum(), g["D"].std()], axis=1) - expected.columns = MultiIndex.from_tuples([("C", "sum"), ("C", "std")]) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = g["D"].agg({"C": ["sum", "std"]}) - tm.assert_frame_equal(result, expected, check_like=True) - - expected = pd.concat([g["D"].sum(), g["D"].std()], axis=1) - expected.columns = ["C", "D"] + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + g["D"].agg({"C": ["sum", "std"]}) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = g["D"].agg({"C": "sum", "D": "std"}) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(SpecificationError, match=msg): + g["D"].agg({"C": "sum", "D": "std"}) def test_agg_nested_dicts(): @@ -278,29 +267,20 @@ def test_agg_nested_dicts(): g = df.groupby(["A", "B"]) - msg = r"cannot perform renaming for r[1-2] with a nested dictionary" + msg = r"nested renamer is not supported" with pytest.raises(SpecificationError, match=msg): g.aggregate({"r1": {"C": ["mean", "sum"]}, "r2": {"D": ["mean", "sum"]}}) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = g.agg({"C": {"ra": ["mean", "std"]}, "D": {"rb": ["mean", "std"]}}) - expected = pd.concat( - [g["C"].mean(), g["C"].std(), g["D"].mean(), g["D"].std()], axis=1 - ) - expected.columns = pd.MultiIndex.from_tuples( - [("ra", "mean"), ("ra", "std"), ("rb", "mean"), ("rb", "std")] - ) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(SpecificationError, match=msg): + g.agg({"C": {"ra": ["mean", "std"]}, "D": {"rb": ["mean", "std"]}}) # same name as the original column # GH9052 - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - expected = g["D"].agg({"result1": np.sum, "result2": np.mean}) - expected = expected.rename(columns={"result1": "D"}) + with pytest.raises(SpecificationError, match=msg): + g["D"].agg({"result1": np.sum, "result2": np.mean}) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = g["D"].agg({"D": np.sum, "result2": np.mean}) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(SpecificationError, match=msg): + g["D"].agg({"D": np.sum, "result2": np.mean}) def test_agg_item_by_item_raise_typeerror(): diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index e17181f55fdba..0d68ff36dfa20 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -10,6 +10,7 @@ import pandas as pd from pandas import DataFrame, Index, MultiIndex, Series, Timestamp, date_range, read_csv +from pandas.core.base import SpecificationError import pandas.core.common as com import pandas.util.testing as tm @@ -55,8 +56,9 @@ def test_basic(dtype): # complex agg agged = grouped.aggregate([np.mean, np.std]) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - agged = grouped.aggregate({"one": np.mean, "two": np.std}) + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + grouped.aggregate({"one": np.mean, "two": np.std}) group_constants = {0: 10, 1: 20, 2: 30} agged = grouped.agg(lambda x: group_constants[x.name] + x.mean()) @@ -452,9 +454,9 @@ def test_frame_set_name_single(df): result = grouped["C"].agg([np.mean, np.std]) assert result.index.name == "A" - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = grouped["C"].agg({"foo": np.mean, "bar": np.std}) - assert result.index.name == "A" + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + grouped["C"].agg({"foo": np.mean, "bar": np.std}) def test_multi_func(df): @@ -602,12 +604,10 @@ def test_groupby_as_index_agg(df): tm.assert_frame_equal(result2, expected2) grouped = df.groupby("A", as_index=True) - expected3 = grouped["C"].sum() - expected3 = DataFrame(expected3).rename(columns={"C": "Q"}) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result3 = grouped["C"].agg({"Q": np.sum}) - tm.assert_frame_equal(result3, expected3) + msg = r"nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + grouped["C"].agg({"Q": np.sum}) # multi-key diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index cbdfbb7a3100b..8e1774d8ee5b7 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -247,10 +247,9 @@ def test_agg_consistency(): r = df.resample("3T") - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - expected = r[["A", "B", "C"]].agg({"r1": "mean", "r2": "sum"}) - result = r.agg({"r1": "mean", "r2": "sum"}) - tm.assert_frame_equal(result, expected, check_like=True) + msg = "nested renamer is not supported" + with pytest.raises(pd.core.base.SpecificationError, match=msg): + r.agg({"r1": "mean", "r2": "sum"}) # TODO: once GH 14008 is fixed, move these tests into @@ -307,26 +306,23 @@ def test_agg(): result = t["A"].aggregate(["mean", "sum"]) tm.assert_frame_equal(result, expected) - expected = pd.concat([a_mean, a_sum], axis=1) - expected.columns = pd.MultiIndex.from_tuples([("A", "mean"), ("A", "sum")]) + msg = "nested renamer is not supported" for t in cases: - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t.aggregate({"A": {"mean": "mean", "sum": "sum"}}) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t.aggregate({"A": {"mean": "mean", "sum": "sum"}}) expected = pd.concat([a_mean, a_sum, b_mean, b_sum], axis=1) expected.columns = pd.MultiIndex.from_tuples( [("A", "mean"), ("A", "sum"), ("B", "mean2"), ("B", "sum2")] ) for t in cases: - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t.aggregate( + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t.aggregate( { "A": {"mean": "mean", "sum": "sum"}, "B": {"mean2": "mean", "sum2": "sum"}, } ) - tm.assert_frame_equal(result, expected, check_like=True) expected = pd.concat([a_mean, a_std, b_mean, b_std], axis=1) expected.columns = pd.MultiIndex.from_tuples( @@ -383,12 +379,10 @@ def test_agg_misc(): [("result1", "A"), ("result1", "B"), ("result2", "A"), ("result2", "B")] ) + msg = "nested renamer is not supported" for t in cases: - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t[["A", "B"]].agg( - OrderedDict([("result1", np.sum), ("result2", np.mean)]) - ) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t[["A", "B"]].agg(OrderedDict([("result1", np.sum), ("result2", np.mean)])) # agg with different hows expected = pd.concat( @@ -408,21 +402,11 @@ def test_agg_misc(): # series like aggs for t in cases: - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t["A"].agg({"A": ["sum", "std"]}) - expected = pd.concat([t["A"].sum(), t["A"].std()], axis=1) - expected.columns = pd.MultiIndex.from_tuples([("A", "sum"), ("A", "std")]) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t["A"].agg({"A": ["sum", "std"]}) - expected = pd.concat( - [t["A"].agg(["sum", "std"]), t["A"].agg(["mean", "std"])], axis=1 - ) - expected.columns = pd.MultiIndex.from_tuples( - [("A", "sum"), ("A", "std"), ("B", "mean"), ("B", "std")] - ) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t["A"].agg({"A": ["sum", "std"], "B": ["mean", "std"]}) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t["A"].agg({"A": ["sum", "std"], "B": ["mean", "std"]}) # errors # invalid names in the agg specification @@ -451,28 +435,20 @@ def test_agg_nested_dicts(): df.groupby(pd.Grouper(freq="2D")), ] - msg = r"cannot perform renaming for r(1|2) with a nested dictionary" + msg = "nested renamer is not supported" for t in cases: with pytest.raises(pd.core.base.SpecificationError, match=msg): t.aggregate({"r1": {"A": ["mean", "sum"]}, "r2": {"B": ["mean", "sum"]}}) for t in cases: - expected = pd.concat( - [t["A"].mean(), t["A"].std(), t["B"].mean(), t["B"].std()], axis=1 - ) - expected.columns = pd.MultiIndex.from_tuples( - [("ra", "mean"), ("ra", "std"), ("rb", "mean"), ("rb", "std")] - ) - - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t[["A", "B"]].agg( + + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t[["A", "B"]].agg( {"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}} ) - tm.assert_frame_equal(result, expected, check_like=True) - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = t.agg({"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}}) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(pd.core.base.SpecificationError, match=msg): + t.agg({"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}}) def test_try_aggregate_non_existing_column(): diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 09c5247ef616a..bdbfa333ef33a 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -7,6 +7,7 @@ import pandas as pd from pandas import DataFrame, Index, Series, isna from pandas.conftest import _get_cython_table_params +from pandas.core.base import SpecificationError import pandas.util.testing as tm @@ -157,7 +158,8 @@ def test_apply_dict_depr(self): columns=["A", "B", "C"], index=pd.date_range("1/1/2000", periods=10), ) - with tm.assert_produces_warning(FutureWarning): + msg = "nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): tsdf.A.agg({"foo": ["sum", "mean"]}) @pytest.mark.parametrize("series", [["1-1", "1-1", np.NaN], ["1-1", "1-2", np.NaN]]) @@ -256,31 +258,17 @@ def test_demo(self): tm.assert_series_equal(result, expected) # nested renaming - with tm.assert_produces_warning(FutureWarning): - result = s.agg({"foo": ["min", "max"]}) - - expected = ( - DataFrame({"foo": [0, 5]}, index=["min", "max"]).unstack().rename("series") - ) - tm.assert_series_equal(result, expected) + msg = "nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + s.agg({"foo": ["min", "max"]}) def test_multiple_aggregators_with_dict_api(self): s = Series(range(6), dtype="int64", name="series") # nested renaming - with tm.assert_produces_warning(FutureWarning): - result = s.agg({"foo": ["min", "max"], "bar": ["sum", "mean"]}) - - expected = ( - DataFrame( - {"foo": [5.0, np.nan, 0.0, np.nan], "bar": [np.nan, 2.5, np.nan, 15.0]}, - columns=["foo", "bar"], - index=["max", "mean", "min", "sum"], - ) - .unstack() - .rename("series") - ) - tm.assert_series_equal(result.reindex_like(expected), expected) + msg = "nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): + s.agg({"foo": ["min", "max"], "bar": ["sum", "mean"]}) def test_agg_apply_evaluate_lambdas_the_same(self, string_series): # test that we are evaluating row-by-row first diff --git a/pandas/tests/window/test_api.py b/pandas/tests/window/test_api.py index 11527efa4c39f..5085576cc96f0 100644 --- a/pandas/tests/window/test_api.py +++ b/pandas/tests/window/test_api.py @@ -1,6 +1,4 @@ from collections import OrderedDict -import warnings -from warnings import catch_warnings import numpy as np import pytest @@ -82,7 +80,6 @@ def test_agg(self): a_sum = r["A"].sum() b_mean = r["B"].mean() b_std = r["B"].std() - b_sum = r["B"].sum() result = r.aggregate([np.mean, np.std]) expected = concat([a_mean, a_std, b_mean, b_std], axis=1) @@ -104,26 +101,18 @@ def test_agg(self): expected.columns = ["mean", "sum"] tm.assert_frame_equal(result, expected) - with catch_warnings(record=True): + msg = "nested renamer is not supported" + with pytest.raises(SpecificationError, match=msg): # using a dict with renaming - warnings.simplefilter("ignore", FutureWarning) - result = r.aggregate({"A": {"mean": "mean", "sum": "sum"}}) - expected = concat([a_mean, a_sum], axis=1) - expected.columns = pd.MultiIndex.from_tuples([("A", "mean"), ("A", "sum")]) - tm.assert_frame_equal(result, expected, check_like=True) + r.aggregate({"A": {"mean": "mean", "sum": "sum"}}) - with catch_warnings(record=True): - warnings.simplefilter("ignore", FutureWarning) - result = r.aggregate( + with pytest.raises(SpecificationError, match=msg): + r.aggregate( { "A": {"mean": "mean", "sum": "sum"}, "B": {"mean2": "mean", "sum2": "sum"}, } ) - expected = concat([a_mean, a_sum, b_mean, b_sum], axis=1) - exp_cols = [("A", "mean"), ("A", "sum"), ("B", "mean2"), ("B", "sum2")] - expected.columns = pd.MultiIndex.from_tuples(exp_cols) - tm.assert_frame_equal(result, expected, check_like=True) result = r.aggregate({"A": ["mean", "std"], "B": ["mean", "std"]}) expected = concat([a_mean, a_std, b_mean, b_std], axis=1) @@ -168,7 +157,7 @@ def test_agg_nested_dicts(self): df = DataFrame({"A": range(5), "B": range(0, 10, 2)}) r = df.rolling(window=3) - msg = r"cannot perform renaming for (r1|r2) with a nested dictionary" + msg = "nested renamer is not supported" with pytest.raises(SpecificationError, match=msg): r.aggregate({"r1": {"A": ["mean", "sum"]}, "r2": {"B": ["mean", "sum"]}}) @@ -178,25 +167,13 @@ def test_agg_nested_dicts(self): expected.columns = pd.MultiIndex.from_tuples( [("ra", "mean"), ("ra", "std"), ("rb", "mean"), ("rb", "std")] ) - with catch_warnings(record=True): - warnings.simplefilter("ignore", FutureWarning) - result = r[["A", "B"]].agg( + with pytest.raises(SpecificationError, match=msg): + r[["A", "B"]].agg( {"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}} ) - tm.assert_frame_equal(result, expected, check_like=True) - with catch_warnings(record=True): - warnings.simplefilter("ignore", FutureWarning) - result = r.agg({"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}}) - expected.columns = pd.MultiIndex.from_tuples( - [ - ("A", "ra", "mean"), - ("A", "ra", "std"), - ("B", "rb", "mean"), - ("B", "rb", "std"), - ] - ) - tm.assert_frame_equal(result, expected, check_like=True) + with pytest.raises(SpecificationError, match=msg): + r.agg({"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}}) def test_count_nonnumeric_types(self): # GH12541 From 646323c438c260ee79db3856b2f07e17accab21e Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 18 Nov 2019 15:52:08 -0800 Subject: [PATCH 2/4] fix check --- pandas/core/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index a7b866ae492ec..c9855701eeb03 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -327,7 +327,6 @@ def _aggregate(self, arg, *args, **kwargs): return self._try_aggregate_string_function(arg, *args, **kwargs), None if isinstance(arg, dict): - # aggregate based on the passed dict if _axis != 0: # pragma: no cover raise ValueError("Can only pass dict with axis=0") @@ -355,7 +354,9 @@ def _aggregate(self, arg, *args, **kwargs): # not ok # {'ra' : { 'A' : 'mean' }} - if isinstance(v, (dict, ABCSeries)): + if isinstance(v, dict): + raise SpecificationError("nested renamer is not supported") + elif isinstance(obj, ABCSeries): raise SpecificationError("nested renamer is not supported") elif isinstance(obj, ABCDataFrame) and k not in obj.columns: raise KeyError("Column '{col}' does not exist!".format(col=k)) From 970592a61d73d32d747ffb223cfe7f6d87a9d2c6 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 18 Nov 2019 17:23:36 -0800 Subject: [PATCH 3/4] dummy commit to force CI From b35ad02657f7db56a5cc8377eca841bd73468c49 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 19 Nov 2019 07:38:48 -0800 Subject: [PATCH 4/4] whatsnew --- doc/source/whatsnew/v1.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index e25049cecdc09..843bd49ed39ab 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -271,6 +271,7 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more. - Removed the previously deprecated ``reduce`` and ``broadcast`` arguments from :meth:`DataFrame.apply` (:issue:`18577`) - Removed the previously deprecated ``assert_raises_regex`` function in ``pandas.util.testing`` (:issue:`29174`) - Removed :meth:`Index.is_lexsorted_for_tuple` (:issue:`29305`) +- Removed support for nexted renaming in :meth:`DataFrame.aggregate`, :meth:`Series.aggregate`, :meth:`DataFrameGroupBy.aggregate`, :meth:`SeriesGroupBy.aggregate`, :meth:`Rolling.aggregate` (:issue:`29608`) - .. _whatsnew_1000.performance: