From b196ed3dfc50990be6d7731b4e41ded629f78f95 Mon Sep 17 00:00:00 2001 From: Derek Sharp Date: Sat, 21 May 2022 20:34:44 -0400 Subject: [PATCH 1/3] ENH: move SpecificationError from core/base.py to error/__init__.py per GH27656 --- doc/source/reference/testing.rst | 1 + doc/source/whatsnew/v1.5.0.rst | 2 +- pandas/core/apply.py | 10 ++++---- pandas/core/base.py | 4 ---- pandas/core/groupby/generic.py | 2 +- pandas/errors/__init__.py | 24 +++++++++++++++++++ pandas/tests/apply/test_invalid_arg.py | 3 ++- .../tests/groupby/aggregate/test_aggregate.py | 3 ++- pandas/tests/groupby/aggregate/test_other.py | 3 ++- pandas/tests/groupby/test_groupby.py | 6 +++-- pandas/tests/resample/test_resample_api.py | 14 +++++------ pandas/tests/test_errors.py | 1 + pandas/tests/window/test_api.py | 3 ++- 13 files changed, 52 insertions(+), 24 deletions(-) diff --git a/doc/source/reference/testing.rst b/doc/source/reference/testing.rst index f91db4600daec..f636ee1806c9b 100644 --- a/doc/source/reference/testing.rst +++ b/doc/source/reference/testing.rst @@ -41,6 +41,7 @@ Exceptions and warnings errors.ParserError errors.ParserWarning errors.PerformanceWarning + errors.SpecificationError errors.UnsortedIndexError errors.UnsupportedFunctionCall diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index b23dd5c2f05a6..d62ea65c37870 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -151,7 +151,7 @@ Other enhancements - A :class:`errors.PerformanceWarning` is now thrown when using ``string[pyarrow]`` dtype with methods that don't dispatch to ``pyarrow.compute`` methods (:issue:`42613`) - Added ``numeric_only`` argument to :meth:`Resampler.sum`, :meth:`Resampler.prod`, :meth:`Resampler.min`, :meth:`Resampler.max`, :meth:`Resampler.first`, and :meth:`Resampler.last` (:issue:`46442`) - ``times`` argument in :class:`.ExponentialMovingWindow` now accepts ``np.timedelta64`` (:issue:`47003`) -- :class:`DataError` now exposed in ``pandas.errors`` (:issue:`27656`) +- :class:`DataError` and :class:`SpecificationError` are now exposed in ``pandas.errors`` (:issue:`27656`) .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: diff --git a/pandas/core/apply.py b/pandas/core/apply.py index dbb119de99765..6f42a47fd9a07 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -33,7 +33,10 @@ Axis, NDFrameT, ) -from pandas.errors import DataError +from pandas.errors import ( + DataError, + SpecificationError, +) from pandas.util._decorators import cache_readonly from pandas.util._exceptions import find_stack_level @@ -51,10 +54,7 @@ ) from pandas.core.algorithms import safe_sort -from pandas.core.base import ( - SelectionMixin, - SpecificationError, -) +from pandas.core.base import SelectionMixin import pandas.core.common as com from pandas.core.construction import ( create_series_with_explicit_dtype, diff --git a/pandas/core/base.py b/pandas/core/base.py index 38cf41a626deb..d65b920a9cada 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -176,10 +176,6 @@ def __setattr__(self, key: str, value): object.__setattr__(self, key, value) -class SpecificationError(Exception): - pass - - class SelectionMixin(Generic[NDFrameT]): """ mixin implementing the selection & aggregation interface on a group-like diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 2acf5c826eb57..ccfa8c1680341 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -37,6 +37,7 @@ Manager2D, SingleManager, ) +from pandas.errors import SpecificationError from pandas.util._decorators import ( Appender, Substitution, @@ -68,7 +69,6 @@ reconstruct_func, validate_func_kwargs, ) -from pandas.core.base import SpecificationError import pandas.core.common as com from pandas.core.construction import create_series_with_explicit_dtype from pandas.core.frame import DataFrame diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index daef5a38c33c2..6cea2e839cd1b 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -244,3 +244,27 @@ class DataError(Exception): Or, it can be raised when trying to apply a function to a non-numerical column on a rolling window. """ + + +class SpecificationError(Exception): + """ + Exception raised in two scenarios. The first way is calling agg on a + Dataframe or Series using a nested renamer (dict-of-dict). + The second way is calling agg on a Dataframe with duplicated functions + names without assigning column name. + + Examples + -------- + >>> df = pd.DataFrame({'A': [1, 1, 1, 2, 2], + ... 'B': range(5), + ... 'C':range(5)}) + >>> df.groupby('A').B.agg({'foo': 'count'}) + ... # SpecificationError + + >>> df.groupby('A').agg({'B': {'foo': ['sum', 'max']}, + ... 'C': {'bar': ['count', 'min']}}) + ... # SpecificationError + + >>> df.groupby('A').agg(['min', 'min']) + ... # SpecificationError + """ diff --git a/pandas/tests/apply/test_invalid_arg.py b/pandas/tests/apply/test_invalid_arg.py index 6ad46ed209cf7..5a498aa7d6595 100644 --- a/pandas/tests/apply/test_invalid_arg.py +++ b/pandas/tests/apply/test_invalid_arg.py @@ -12,6 +12,8 @@ import numpy as np import pytest +from pandas.errors import SpecificationError + from pandas import ( Categorical, DataFrame, @@ -20,7 +22,6 @@ notna, ) import pandas._testing as tm -from pandas.core.base import SpecificationError @pytest.mark.parametrize("result_type", ["foo", 1]) diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 55aad403f1492..d52b6ceaf8990 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -9,6 +9,8 @@ import numpy as np import pytest +from pandas.errors import SpecificationError + from pandas.core.dtypes.common import is_integer_dtype import pandas as pd @@ -21,7 +23,6 @@ to_datetime, ) import pandas._testing as tm -from pandas.core.base import SpecificationError from pandas.core.groupby.grouper import Grouping diff --git a/pandas/tests/groupby/aggregate/test_other.py b/pandas/tests/groupby/aggregate/test_other.py index 06044ddd3f4b8..f84abecea37da 100644 --- a/pandas/tests/groupby/aggregate/test_other.py +++ b/pandas/tests/groupby/aggregate/test_other.py @@ -8,6 +8,8 @@ import numpy as np import pytest +from pandas.errors import SpecificationError + import pandas as pd from pandas import ( DataFrame, @@ -19,7 +21,6 @@ period_range, ) import pandas._testing as tm -from pandas.core.base import SpecificationError from pandas.io.formats.printing import pprint_thing diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index 61951292d55a8..97e616ef14cef 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -6,7 +6,10 @@ from pandas._libs import lib from pandas.compat import IS64 -from pandas.errors import PerformanceWarning +from pandas.errors import ( + PerformanceWarning, + SpecificationError, +) import pandas as pd from pandas import ( @@ -24,7 +27,6 @@ ) import pandas._testing as tm from pandas.core.arrays import BooleanArray -from pandas.core.base import SpecificationError import pandas.core.common as com from pandas.core.groupby.base import maybe_normalize_deprecated_kernels diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 04b629d089925..36f67664a3260 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -427,7 +427,7 @@ def test_agg(): msg = "nested renamer is not supported" for t in cases: - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t.aggregate({"A": {"mean": "mean", "sum": "sum"}}) expected = pd.concat([a_mean, a_sum, b_mean, b_sum], axis=1) @@ -435,7 +435,7 @@ def test_agg(): [("A", "mean"), ("A", "sum"), ("B", "mean2"), ("B", "sum2")] ) for t in cases: - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t.aggregate( { "A": {"mean": "mean", "sum": "sum"}, @@ -539,10 +539,10 @@ def test_agg_misc(): # series like aggs for t in cases: - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t["A"].agg({"A": ["sum", "std"]}) - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t["A"].agg({"A": ["sum", "std"], "B": ["mean", "std"]}) # errors @@ -588,17 +588,17 @@ def test_agg_nested_dicts(): msg = "nested renamer is not supported" for t in cases: - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t.aggregate({"r1": {"A": ["mean", "sum"]}, "r2": {"B": ["mean", "sum"]}}) for t in cases: - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t[["A", "B"]].agg( {"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}} ) - with pytest.raises(pd.core.base.SpecificationError, match=msg): + with pytest.raises(pd.errors.SpecificationError, match=msg): t.agg({"A": {"ra": ["mean", "std"]}, "B": {"rb": ["mean", "std"]}}) diff --git a/pandas/tests/test_errors.py b/pandas/tests/test_errors.py index a1ce5b7a239fe..140297297fdbc 100644 --- a/pandas/tests/test_errors.py +++ b/pandas/tests/test_errors.py @@ -20,6 +20,7 @@ "OptionError", "NumbaUtilError", "DataError", + "SpecificationError", ], ) def test_exception_importable(exc): diff --git a/pandas/tests/window/test_api.py b/pandas/tests/window/test_api.py index 931e52e4efd18..2a8caa1d42d4d 100644 --- a/pandas/tests/window/test_api.py +++ b/pandas/tests/window/test_api.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.errors import SpecificationError + from pandas import ( DataFrame, Index, @@ -13,7 +15,6 @@ timedelta_range, ) import pandas._testing as tm -from pandas.core.base import SpecificationError def test_getitem(step): From 11601d2a68ad6bb5b198a121286b3db5d34ed72c Mon Sep 17 00:00:00 2001 From: Derek Sharp Date: Wed, 25 May 2022 17:30:58 -0400 Subject: [PATCH 2/3] ENH: fix doc string --- pandas/errors/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 6cea2e839cd1b..23893ef34246e 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -258,13 +258,12 @@ class SpecificationError(Exception): >>> df = pd.DataFrame({'A': [1, 1, 1, 2, 2], ... 'B': range(5), ... 'C':range(5)}) - >>> df.groupby('A').B.agg({'foo': 'count'}) - ... # SpecificationError + >>> df.groupby('A').B.agg({'foo': 'count'}) # doctest: +SKIP + ... # SpecificationError: nested renamer is not supported - >>> df.groupby('A').agg({'B': {'foo': ['sum', 'max']}, - ... 'C': {'bar': ['count', 'min']}}) - ... # SpecificationError + >>> df.groupby('A').agg({'B': {'foo': ['sum', 'max']}}) # doctest: +SKIP + ... # SpecificationError: nested renamer is not supported - >>> df.groupby('A').agg(['min', 'min']) - ... # SpecificationError + >>> df.groupby('A').agg(['min', 'min']) # doctest: +SKIP + ... # SpecificationError: nested renamer is not supported """ From 3250e1deb031b597ad20260b72f0b98a12b60296 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Thu, 26 May 2022 10:44:50 -0700 Subject: [PATCH 3/3] Update pandas/errors/__init__.py --- pandas/errors/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 23893ef34246e..a948f0f46e21a 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -257,7 +257,7 @@ class SpecificationError(Exception): -------- >>> df = pd.DataFrame({'A': [1, 1, 1, 2, 2], ... 'B': range(5), - ... 'C':range(5)}) + ... 'C': range(5)}) >>> df.groupby('A').B.agg({'foo': 'count'}) # doctest: +SKIP ... # SpecificationError: nested renamer is not supported