From 72512b79e60224f1ad060c20896f8ceaeaa16628 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 15 Sep 2020 17:32:04 -0400 Subject: [PATCH 1/9] CLN: Refactor and typing in aggregate.transform --- doc/source/whatsnew/v1.2.0.rst | 2 +- pandas/core/aggregation.py | 123 +++++++++++++++++++++------------ pandas/core/frame.py | 2 +- pandas/core/shared_docs.py | 9 +-- 4 files changed, 86 insertions(+), 50 deletions(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 5882b74aa8b05..75ea48d3655c2 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -349,6 +349,7 @@ Reshaping - Bug in :func:`union_indexes` where input index names are not preserved in some cases. Affects :func:`concat` and :class:`DataFrame` constructor (:issue:`13475`) - Bug in func :meth:`crosstab` when using multiple columns with ``margins=True`` and ``normalize=True`` (:issue:`35144`) - Bug in :meth:`DataFrame.agg` with ``func={'name':}`` incorrectly raising ``TypeError`` when ``DataFrame.columns==['Name']`` (:issue:`36212`) +- Bug in :meth:`Series.transform` would give incorrect results or raise when the argument ``func`` was dictionary (:issue:`35811`) - Sparse @@ -368,7 +369,6 @@ Other ^^^^^ - Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` incorrectly raising ``AssertionError`` instead of ``ValueError`` when invalid parameter combinations are passed (:issue:`36045`) - Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` with numeric values and string ``to_replace`` (:issue:`34789`) -- Bug in :meth:`Series.transform` would give incorrect results or raise when the argument ``func`` was dictionary (:issue:`35811`) - Bug in :meth:`Index.union` behaving differently depending on whether operand is a :class:`Index` or other list-like (:issue:`36384`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index c123156495924..ceba0dd003a1b 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -16,9 +16,17 @@ Sequence, Tuple, Union, + cast, ) -from pandas._typing import AggFuncType, Axis, FrameOrSeries, Label +from pandas._typing import ( + AggFuncType, + AggFuncTypeBase, + Axis, + FrameOrSeries, + FrameOrSeriesUnion, + Label, +) from pandas.core.dtypes.common import is_dict_like, is_list_like from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries @@ -388,7 +396,7 @@ def validate_func_kwargs( def transform( obj: FrameOrSeries, func: AggFuncType, axis: Axis, *args, **kwargs, -) -> FrameOrSeries: +) -> FrameOrSeriesUnion: """ Transform a DataFrame or Series @@ -415,67 +423,94 @@ def transform( ValueError If the transform function fails or does not transform. """ - from pandas.core.reshape.concat import concat - is_series = obj.ndim == 1 - if obj._get_axis_number(axis) == 1: assert not is_series return transform(obj.T, func, 0, *args, **kwargs).T - if isinstance(func, list): + if is_list_like(func) and not is_dict_like(func): + func = cast(List[AggFuncTypeBase], func) + # Convert func equivalent dict if is_series: func = {com.get_callable_name(v) or v: v for v in func} else: func = {col: func for col in obj} - if isinstance(func, dict): - if not is_series: - cols = sorted(set(func.keys()) - set(obj.columns)) - if len(cols) > 0: - raise SpecificationError(f"Column(s) {cols} do not exist") - - if any(isinstance(v, dict) for v in func.values()): - # GH 15931 - deprecation of renaming keys - raise SpecificationError("nested renamer is not supported") - - results = {} - for name, how in func.items(): - colg = obj._gotitem(name, ndim=1) - try: - results[name] = transform(colg, how, 0, *args, **kwargs) - except Exception as e: - if str(e) == "Function did not transform": - raise e - - # combine results - if len(results) == 0: + if is_dict_like(func): + func = cast(Dict[Label, Union[AggFuncTypeBase, List[AggFuncTypeBase]]], func) + result = transform_dict_like(obj, func, *args, **kwargs) + else: + func = cast(AggFuncTypeBase, func) + try: + result = transform_str_or_callable(obj, func, *args, **kwargs) + except Exception: raise ValueError("Transform function failed") - return concat(results, axis=1) - - # func is either str or callable - try: - if isinstance(func, str): - result = obj._try_aggregate_string_function(func, *args, **kwargs) - else: - f = obj._get_cython_func(func) - if f and not args and not kwargs: - result = getattr(obj, f)() - else: - try: - result = obj.apply(func, args=args, **kwargs) - except Exception: - result = func(obj, *args, **kwargs) - except Exception: - raise ValueError("Transform function failed") # Functions that transform may return empty Series/DataFrame # when the dtype is not appropriate if isinstance(result, (ABCSeries, ABCDataFrame)) and result.empty: raise ValueError("Transform function failed") + if not isinstance(result, (ABCSeries, ABCDataFrame)) or not result.index.equals( obj.index ): raise ValueError("Function did not transform") return result + + +def transform_dict_like( + obj: FrameOrSeries, + func: Dict[Label, Union[AggFuncTypeBase, List[AggFuncTypeBase]]], + *args, + **kwargs, +): + """ + Compute transform in the case of a dict-like func + """ + from pandas.core.reshape.concat import concat + + if obj.ndim != 1: + # Check for missing columns on a frame + cols = sorted(set(func.keys()) - set(obj.columns)) + if len(cols) > 0: + raise SpecificationError(f"Column(s) {cols} do not exist") + + if any(isinstance(v, dict) for v in func.values()): + # GH 15931 - deprecation of renaming keys + raise SpecificationError("nested renamer is not supported") + + results: Dict[Label, FrameOrSeriesUnion] = {} + for name, how in func.items(): + colg = obj._gotitem(name, ndim=1) + try: + results[name] = transform(colg, how, 0, *args, **kwargs) + except Exception as e: + if str(e) == "Function did not transform": + raise e + + # combine results + if len(results) == 0: + raise ValueError("Transform function failed") + return concat(results, axis=1) + + +def transform_str_or_callable( + obj: FrameOrSeries, func: AggFuncTypeBase, *args, **kwargs +) -> FrameOrSeriesUnion: + """ + Compute transform in the case of a string or callable func + """ + if isinstance(func, str): + return obj._try_aggregate_string_function(func, *args, **kwargs) + + if not args and not kwargs: + f = obj._get_cython_func(func) + if f: + return getattr(obj, f)() + + # Two possible ways to use a UDF - apply or call directly + try: + return obj.apply(func, args=args, **kwargs) + except Exception: + return func(obj, *args, **kwargs) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 36dfe43bfd708..0c2ad0532b3ba 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -7255,7 +7255,7 @@ def diff(self, periods: int = 1, axis: Axis = 0) -> DataFrame: def _gotitem( self, - key: Union[str, List[str]], + key: Union[Label, List[Label]], ndim: int, subset: Optional[FrameOrSeriesUnion] = None, ) -> FrameOrSeriesUnion: diff --git a/pandas/core/shared_docs.py b/pandas/core/shared_docs.py index 14363dabfcdf3..d595b03a535ed 100644 --- a/pandas/core/shared_docs.py +++ b/pandas/core/shared_docs.py @@ -265,16 +265,17 @@ Parameters ---------- -func : function, str, list or dict +func : function, str, list-like or dict-like Function to use for transforming the data. If a function, must either - work when passed a {klass} or when passed to {klass}.apply. + work when passed a {klass} or when passed to {klass}.apply. If func + is both list-like and dict-like, dict-like behavior takes precedence. Accepted combinations are: - function - string function name - - list of functions and/or function names, e.g. ``[np.exp, 'sqrt']`` - - dict of axis labels -> functions, function names or list of such. + - list-like of functions and/or function names, e.g. ``[np.exp, 'sqrt']`` + - dict-like of axis labels -> functions, function names or list-like of such. {axis} *args Positional arguments to pass to `func`. From 061f9c6331651b26200d636c71c203a7f3a45a8b Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 20 Sep 2020 08:33:52 -0400 Subject: [PATCH 2/9] Added tests for list-like(ndarray) and dict-like (Series) --- pandas/core/aggregation.py | 3 ++- .../tests/frame/apply/test_frame_transform.py | 17 ++++++++++++----- .../tests/series/apply/test_series_transform.py | 15 +++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index ceba0dd003a1b..8b7c2e0f41683 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -476,7 +476,8 @@ def transform_dict_like( if len(cols) > 0: raise SpecificationError(f"Column(s) {cols} do not exist") - if any(isinstance(v, dict) for v in func.values()): + # Can't use func.values(); wouldn't work for a Series + if any(is_dict_like(v) for _, v in func.items()): # GH 15931 - deprecation of renaming keys raise SpecificationError("nested renamer is not supported") diff --git a/pandas/tests/frame/apply/test_frame_transform.py b/pandas/tests/frame/apply/test_frame_transform.py index 346e60954fc13..1889fef81ac0c 100644 --- a/pandas/tests/frame/apply/test_frame_transform.py +++ b/pandas/tests/frame/apply/test_frame_transform.py @@ -4,7 +4,7 @@ import numpy as np import pytest -from pandas import DataFrame, MultiIndex +from pandas import DataFrame, MultiIndex, Series import pandas._testing as tm from pandas.core.base import SpecificationError from pandas.core.groupby.base import transformation_kernels @@ -41,9 +41,15 @@ def test_transform_groupby_kernel(axis, float_frame, op): @pytest.mark.parametrize( - "ops, names", [([np.sqrt], ["sqrt"]), ([np.abs, np.sqrt], ["absolute", "sqrt"])] + "ops, names", + [ + ([np.sqrt], ["sqrt"]), + ([np.abs, np.sqrt], ["absolute", "sqrt"]), + (np.array([np.sqrt]), ["sqrt"]), + (np.array([np.abs, np.sqrt]), ["absolute", "sqrt"]), + ], ) -def test_transform_list(axis, float_frame, ops, names): +def test_transform_listlike(axis, float_frame, ops, names): # GH 35964 other_axis = 1 if axis in {0, "index"} else 0 with np.errstate(all="ignore"): @@ -56,7 +62,8 @@ def test_transform_list(axis, float_frame, ops, names): tm.assert_frame_equal(result, expected) -def test_transform_dict(axis, float_frame): +@pytest.mark.parametrize("argtype", [dict, Series]) +def test_transform_dictlike(axis, float_frame, argtype): # GH 35964 if axis == 0 or axis == "index": e = float_frame.columns[0] @@ -64,7 +71,7 @@ def test_transform_dict(axis, float_frame): else: e = float_frame.index[0] expected = float_frame.iloc[[0]].transform(np.abs) - result = float_frame.transform({e: np.abs}, axis=axis) + result = float_frame.transform(argtype({e: np.abs}), axis=axis) tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/series/apply/test_series_transform.py b/pandas/tests/series/apply/test_series_transform.py index 0842674da2a7d..0c47b8a4cec39 100644 --- a/pandas/tests/series/apply/test_series_transform.py +++ b/pandas/tests/series/apply/test_series_transform.py @@ -34,9 +34,15 @@ def test_transform_groupby_kernel(string_series, op): @pytest.mark.parametrize( - "ops, names", [([np.sqrt], ["sqrt"]), ([np.abs, np.sqrt], ["absolute", "sqrt"])] + "ops, names", + [ + ([np.sqrt], ["sqrt"]), + ([np.abs, np.sqrt], ["absolute", "sqrt"]), + (np.array([np.sqrt]), ["sqrt"]), + (np.array([np.abs, np.sqrt]), ["absolute", "sqrt"]), + ], ) -def test_transform_list(string_series, ops, names): +def test_transform_listlike(string_series, ops, names): # GH 35964 with np.errstate(all="ignore"): expected = concat([op(string_series) for op in ops], axis=1) @@ -45,12 +51,13 @@ def test_transform_list(string_series, ops, names): tm.assert_frame_equal(result, expected) -def test_transform_dict(string_series): +@pytest.mark.parametrize("argtype", [dict, Series]) +def test_transform_dictlike(string_series, argtype): # GH 35964 with np.errstate(all="ignore"): expected = concat([np.sqrt(string_series), np.abs(string_series)], axis=1) expected.columns = ["foo", "bar"] - result = string_series.transform({"foo": np.sqrt, "bar": np.abs}) + result = string_series.transform(argtype({"foo": np.sqrt, "bar": np.abs})) tm.assert_frame_equal(result, expected) From 2b2547f9857c92f1f1d0f0015b2be0f2111166aa Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 22 Sep 2020 17:14:15 -0400 Subject: [PATCH 3/9] argtype -> box --- pandas/tests/frame/apply/test_frame_transform.py | 6 +++--- pandas/tests/series/apply/test_series_transform.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/tests/frame/apply/test_frame_transform.py b/pandas/tests/frame/apply/test_frame_transform.py index 1889fef81ac0c..c73c9559cb383 100644 --- a/pandas/tests/frame/apply/test_frame_transform.py +++ b/pandas/tests/frame/apply/test_frame_transform.py @@ -62,8 +62,8 @@ def test_transform_listlike(axis, float_frame, ops, names): tm.assert_frame_equal(result, expected) -@pytest.mark.parametrize("argtype", [dict, Series]) -def test_transform_dictlike(axis, float_frame, argtype): +@pytest.mark.parametrize("box", [dict, Series]) +def test_transform_dictlike(axis, float_frame, box): # GH 35964 if axis == 0 or axis == "index": e = float_frame.columns[0] @@ -71,7 +71,7 @@ def test_transform_dictlike(axis, float_frame, argtype): else: e = float_frame.index[0] expected = float_frame.iloc[[0]].transform(np.abs) - result = float_frame.transform(argtype({e: np.abs}), axis=axis) + result = float_frame.transform(box({e: np.abs}), axis=axis) tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/series/apply/test_series_transform.py b/pandas/tests/series/apply/test_series_transform.py index 0c47b8a4cec39..6da4ab0c3544c 100644 --- a/pandas/tests/series/apply/test_series_transform.py +++ b/pandas/tests/series/apply/test_series_transform.py @@ -51,13 +51,13 @@ def test_transform_listlike(string_series, ops, names): tm.assert_frame_equal(result, expected) -@pytest.mark.parametrize("argtype", [dict, Series]) -def test_transform_dictlike(string_series, argtype): +@pytest.mark.parametrize("box", [dict, Series]) +def test_transform_dictlike(string_series, box): # GH 35964 with np.errstate(all="ignore"): expected = concat([np.sqrt(string_series), np.abs(string_series)], axis=1) expected.columns = ["foo", "bar"] - result = string_series.transform(argtype({"foo": np.sqrt, "bar": np.abs})) + result = string_series.transform(box({"foo": np.sqrt, "bar": np.abs})) tm.assert_frame_equal(result, expected) From 431488c76958703e2afe5871a8901d49928ce552 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 23 Sep 2020 16:52:56 -0400 Subject: [PATCH 4/9] Added case and tests for empty list-like or dict-like --- pandas/core/aggregation.py | 9 +++++--- .../tests/frame/apply/test_frame_transform.py | 22 +++++++++++++++++++ .../series/apply/test_series_transform.py | 22 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index fc2fa195a3dc5..4920db68c132d 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -481,14 +481,17 @@ def transform_dict_like( # GH 15931 - deprecation of renaming keys raise SpecificationError("nested renamer is not supported") + if len(func) == 0: + raise ValueError("no results") + results: Dict[Label, FrameOrSeriesUnion] = {} for name, how in func.items(): colg = obj._gotitem(name, ndim=1) try: results[name] = transform(colg, how, 0, *args, **kwargs) - except Exception as e: - if str(e) == "Function did not transform": - raise e + except Exception as err: + if str(err) == "Function did not transform" or str(err) == "no results": + raise err # combine results if len(results) == 0: diff --git a/pandas/tests/frame/apply/test_frame_transform.py b/pandas/tests/frame/apply/test_frame_transform.py index c73c9559cb383..59fae3e50e317 100644 --- a/pandas/tests/frame/apply/test_frame_transform.py +++ b/pandas/tests/frame/apply/test_frame_transform.py @@ -62,6 +62,12 @@ def test_transform_listlike(axis, float_frame, ops, names): tm.assert_frame_equal(result, expected) +@pytest.mark.parametrize("ops", [[], np.array([])]) +def test_transform_empty_listlike(float_frame, ops): + with pytest.raises(ValueError, match="no results"): + float_frame.transform(ops) + + @pytest.mark.parametrize("box", [dict, Series]) def test_transform_dictlike(axis, float_frame, box): # GH 35964 @@ -75,6 +81,22 @@ def test_transform_dictlike(axis, float_frame, box): tm.assert_frame_equal(result, expected) +@pytest.mark.parametrize( + "ops", + [ + {}, + {"A": []}, + {"A": [], "B": "cumsum"}, + {"A": "cumsum", "B": []}, + {"A": [], "B": ["cumsum"]}, + {"A": ["cumsum"], "B": []}, + ], +) +def test_transform_empty_dictlike(float_frame, ops): + with pytest.raises(ValueError, match="no results"): + float_frame.transform(ops) + + @pytest.mark.parametrize("use_apply", [True, False]) def test_transform_udf(axis, float_frame, use_apply): # GH 35964 diff --git a/pandas/tests/series/apply/test_series_transform.py b/pandas/tests/series/apply/test_series_transform.py index 6da4ab0c3544c..68fa30d5e4cda 100644 --- a/pandas/tests/series/apply/test_series_transform.py +++ b/pandas/tests/series/apply/test_series_transform.py @@ -51,6 +51,12 @@ def test_transform_listlike(string_series, ops, names): tm.assert_frame_equal(result, expected) +@pytest.mark.parametrize("ops", [[], np.array([])]) +def test_transform_empty_listlike(string_series, ops): + with pytest.raises(ValueError, match="no results"): + string_series.transform(ops) + + @pytest.mark.parametrize("box", [dict, Series]) def test_transform_dictlike(string_series, box): # GH 35964 @@ -61,6 +67,22 @@ def test_transform_dictlike(string_series, box): tm.assert_frame_equal(result, expected) +@pytest.mark.parametrize( + "ops", + [ + {}, + {"A": []}, + {"A": [], "B": ["cumsum"]}, + {"A": ["cumsum"], "B": []}, + {"A": [], "B": "cumsum"}, + {"A": "cumsum", "B": []}, + ], +) +def test_transform_empty_dictlike(string_series, ops): + with pytest.raises(ValueError, match="no results"): + string_series.transform(ops) + + def test_transform_udf(axis, string_series): # GH 35964 # via apply From 3e72b805152e4bd3aa4b4a3e88f6919a875c71ca Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 23 Sep 2020 16:56:26 -0400 Subject: [PATCH 5/9] Merge cleanup --- doc/source/whatsnew/v1.2.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 04ad664e8f0ec..22ef3e8a93937 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -249,7 +249,7 @@ Datetimelike - Bug in :class:`DateOffset` where attributes reconstructed from pickle files differ from original objects when input values exceed normal ranges (e.g months=12) (:issue:`34511`) - Bug in :meth:`DatetimeIndex.get_slice_bound` where ``datetime.date`` objects were not accepted or naive :class:`Timestamp` with a tz-aware :class:`DatetimeIndex` (:issue:`35690`) - Bug in :meth:`DatetimeIndex.slice_locs` where ``datetime.date`` objects were not accepted (:issue:`34077`) -- Bug in :meth:`DatetimeIndex.searchsorted`, :meth:`TimedeltaIndex.searchsorted`, :meth:`PeriodIndex.searchsorted`, and :meth:`Series.searchsorted` with ``datetime64``, ``timedelta64`` or ``Period`` dtype placement of ``NaT`` values being inconsistent with ``NumPy`` (:issue:`36176`,:issue:`36254`) +- Bug in :meth:`DatetimeIndex.searchsorted`, :meth:`TimedeltaIndex.searchsorted`, :meth:`PeriodIndex.searchsorted`, and :meth:`Series.searchsorted` with ``datetime64``, ``timedelta64`` or ``Period`` dtype placement of ``NaT`` values being inconsistent with ``NumPy`` (:issue:`36176`, :issue:`36254`) - Inconsistency in :class:`DatetimeArray`, :class:`TimedeltaArray`, and :class:`PeriodArray` setitem casting arrays of strings to datetimelike scalars but not scalar strings (:issue:`36261`) - From 506e804f646627d824f442c4d53e5f10751c6383 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 23 Sep 2020 16:57:09 -0400 Subject: [PATCH 6/9] Merge cleanup --- doc/source/whatsnew/v1.2.0.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 22ef3e8a93937..2e28b23491a64 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -376,7 +376,6 @@ Other - Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` incorrectly raising ``AssertionError`` instead of ``ValueError`` when invalid parameter combinations are passed (:issue:`36045`) - Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` with numeric values and string ``to_replace`` (:issue:`34789`) - Fixed metadata propagation in the :class:`Series.dt` accessor (:issue:`28283`) -- Bug in :meth:`Series.transform` would give incorrect results or raise when the argument ``func`` was dictionary (:issue:`35811`) - Bug in :meth:`Index.union` behaving differently depending on whether operand is a :class:`Index` or other list-like (:issue:`36384`) .. --------------------------------------------------------------------------- From d0193c92ff45cc28c4f593a5cf4cbfd2f980c18f Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 24 Sep 2020 15:53:33 -0400 Subject: [PATCH 7/9] Moved length check --- pandas/core/aggregation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index 4920db68c132d..edc21a2b4c628 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -470,6 +470,9 @@ def transform_dict_like( """ from pandas.core.reshape.concat import concat + if len(func) == 0: + raise ValueError("no results") + if obj.ndim != 1: # Check for missing columns on a frame cols = sorted(set(func.keys()) - set(obj.columns)) @@ -481,9 +484,6 @@ def transform_dict_like( # GH 15931 - deprecation of renaming keys raise SpecificationError("nested renamer is not supported") - if len(func) == 0: - raise ValueError("no results") - results: Dict[Label, FrameOrSeriesUnion] = {} for name, how in func.items(): colg = obj._gotitem(name, ndim=1) From ea2ad3da0bcdbeeb8cf509c20f626074222d7ff9 Mon Sep 17 00:00:00 2001 From: rhshadrach Date: Mon, 28 Sep 2020 19:26:57 -0400 Subject: [PATCH 8/9] cast --- pandas/core/aggregation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index 4f769f6c930c6..8a5378f616bcb 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -445,6 +445,7 @@ def transform( return transform_dict_like(obj, func, *args, **kwargs) # func is either str or callable + func = cast(AggFuncTypeBase, func) try: result = transform_str_or_callable(obj, func, *args, **kwargs) except Exception: From 7066f80ee1acfc430700d1c8d1815ee7f4b460ba Mon Sep 17 00:00:00 2001 From: rhshadrach Date: Mon, 5 Oct 2020 23:19:44 -0400 Subject: [PATCH 9/9] Changed error message --- pandas/core/aggregation.py | 7 +++++-- pandas/tests/frame/apply/test_frame_transform.py | 4 ++-- pandas/tests/series/apply/test_series_transform.py | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index 8a5378f616bcb..ad69e9f31e065 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -475,7 +475,7 @@ def transform_dict_like( from pandas.core.reshape.concat import concat if len(func) == 0: - raise ValueError("no results") + raise ValueError("No transform functions were provided") if obj.ndim != 1: # Check for missing columns on a frame @@ -494,7 +494,10 @@ def transform_dict_like( try: results[name] = transform(colg, how, 0, *args, **kwargs) except Exception as err: - if str(err) == "Function did not transform" or str(err) == "no results": + if ( + str(err) == "Function did not transform" + or str(err) == "No transform functions were provided" + ): raise err # combine results diff --git a/pandas/tests/frame/apply/test_frame_transform.py b/pandas/tests/frame/apply/test_frame_transform.py index 59fae3e50e317..01c6fd4ec08f0 100644 --- a/pandas/tests/frame/apply/test_frame_transform.py +++ b/pandas/tests/frame/apply/test_frame_transform.py @@ -64,7 +64,7 @@ def test_transform_listlike(axis, float_frame, ops, names): @pytest.mark.parametrize("ops", [[], np.array([])]) def test_transform_empty_listlike(float_frame, ops): - with pytest.raises(ValueError, match="no results"): + with pytest.raises(ValueError, match="No transform functions were provided"): float_frame.transform(ops) @@ -93,7 +93,7 @@ def test_transform_dictlike(axis, float_frame, box): ], ) def test_transform_empty_dictlike(float_frame, ops): - with pytest.raises(ValueError, match="no results"): + with pytest.raises(ValueError, match="No transform functions were provided"): float_frame.transform(ops) diff --git a/pandas/tests/series/apply/test_series_transform.py b/pandas/tests/series/apply/test_series_transform.py index 0ece09dd1d640..67b271f757cfb 100644 --- a/pandas/tests/series/apply/test_series_transform.py +++ b/pandas/tests/series/apply/test_series_transform.py @@ -53,7 +53,7 @@ def test_transform_listlike(string_series, ops, names): @pytest.mark.parametrize("ops", [[], np.array([])]) def test_transform_empty_listlike(string_series, ops): - with pytest.raises(ValueError, match="no results"): + with pytest.raises(ValueError, match="No transform functions were provided"): string_series.transform(ops) @@ -79,7 +79,7 @@ def test_transform_dictlike(string_series, box): ], ) def test_transform_empty_dictlike(string_series, ops): - with pytest.raises(ValueError, match="no results"): + with pytest.raises(ValueError, match="No transform functions were provided"): string_series.transform(ops)