From 6f05eba98de4fc31a9bf8945bde433b42ae73a2b Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 23 Apr 2018 13:46:02 -0500 Subject: [PATCH 1/3] BUG: Fixed NDFrame.transform('abs') Closes https://github.com/pandas-dev/pandas/issues/19760 --- pandas/core/apply.py | 4 +++- pandas/tests/frame/test_apply.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 8fb74e2e87174..c14e335955c2d 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -111,7 +111,9 @@ def get_result(self): # string dispatch if isinstance(self.f, compat.string_types): - self.kwds['axis'] = self.axis + if self.f not in {'abs'}: + # Not all transform functions take an axis keyword. + self.kwds['axis'] = self.axis return getattr(self.obj, self.f)(*self.args, **self.kwds) # ufunc diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index a057ca0879cac..938f3122c3658 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -880,6 +880,13 @@ def f(): with np.errstate(all='ignore'): df.agg({'A': ['abs', 'sum'], 'B': ['mean', 'max']}) + def test_transform_abs_name(self): + # https://github.com/pandas-dev/pandas/issues/19760 + df = pd.DataFrame({"A": [-1, 2]}) + result = df.transform('abs') + expected = pd.DataFrame({"A": [1, 2]}) + tm.assert_frame_equal(result, expected) + def test_demo(self): # demonstration tests df = pd.DataFrame({'A': range(5), 'B': 5}) From b26e5b2a5067140281d6af19a89f8acd39a5342c Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 24 Apr 2018 08:23:30 -0500 Subject: [PATCH 2/3] REF: inspect function signature --- pandas/core/apply.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index c14e335955c2d..903861a5caa78 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -111,10 +111,11 @@ def get_result(self): # string dispatch if isinstance(self.f, compat.string_types): - if self.f not in {'abs'}: - # Not all transform functions take an axis keyword. + func = getattr(self.obj, self.f) + sig = compat.signature(func) + if 'axis' in sig.args: self.kwds['axis'] = self.axis - return getattr(self.obj, self.f)(*self.args, **self.kwds) + return func(*self.args, **self.kwds) # ufunc elif isinstance(self.f, np.ufunc): From 715ac25aa808e0fac13e17d1bd998956117a5582 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 25 Apr 2018 10:19:40 -0500 Subject: [PATCH 3/3] parametrized test --- pandas/core/apply.py | 3 +++ pandas/tests/frame/test_apply.py | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 903861a5caa78..ac173c5182bc7 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -111,6 +111,9 @@ def get_result(self): # string dispatch if isinstance(self.f, compat.string_types): + # Support for `frame.transform('method')` + # Some methods (shift, etc.) require the axis argument, others + # don't, so inspect and insert if nescessary. func = getattr(self.obj, self.f) sig = compat.signature(func) if 'axis' in sig.args: diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index 938f3122c3658..af39c8f01cf73 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -4,6 +4,7 @@ import pytest +import operator from datetime import datetime import warnings @@ -880,11 +881,14 @@ def f(): with np.errstate(all='ignore'): df.agg({'A': ['abs', 'sum'], 'B': ['mean', 'max']}) - def test_transform_abs_name(self): + @pytest.mark.parametrize('method', [ + 'abs', 'shift', 'pct_change', 'cumsum', 'rank', + ]) + def test_transform_method_name(self, method): # https://github.com/pandas-dev/pandas/issues/19760 df = pd.DataFrame({"A": [-1, 2]}) - result = df.transform('abs') - expected = pd.DataFrame({"A": [1, 2]}) + result = df.transform(method) + expected = operator.methodcaller(method)(df) tm.assert_frame_equal(result, expected) def test_demo(self):