From 556e46dd45a956972c163f60b9d41142f269962a Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Mon, 12 Sep 2022 10:34:44 +0800 Subject: [PATCH 1/4] Fix bug in pd.eval when negative value in function --- pandas/core/computation/expr.py | 4 ++-- pandas/tests/computation/test_eval.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index ff3f259f49955..c91cfd65e3c40 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -693,7 +693,7 @@ def visit_Call(self, node, side=None, **kwargs): else: - new_args = [self.visit(arg).value for arg in node.args] + new_args = [self.visit(arg)(self.env) for arg in node.args] for key in node.keywords: if not isinstance(key, ast.keyword): @@ -704,7 +704,7 @@ def visit_Call(self, node, side=None, **kwargs): ) if key.arg: - kwargs[key.arg] = self.visit(key.value).value + kwargs[key.arg] = self.visit(key.value)(self.env) name = self.env.add_tmp(res(*new_args, **kwargs)) return self.term_type(name=name, env=self.env) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index b3b80e8e5db65..275dbc0fff403 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -600,6 +600,18 @@ def test_float_comparison_bin_op(self, dtype, expr): res = df.eval(expr) assert res.values == np.array([False]) + def test_unary_in_function(self): + # GH 46471 + df = DataFrame({"x": [0, 1, np.nan]}) + + result = df.eval("x.fillna(-1)") + expected = df.x.fillna(-1) + tm.assert_series_equal(result, expected) + + result = df.eval("x.shift(1, fill_value=-1)") + expected = df.x.shift(1, fill_value=-1) + tm.assert_series_equal(result, expected) + @pytest.mark.parametrize( "ex", ( From 95c86dba0c7267a0587b2cb82b530da4d9c8c93e Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Mon, 12 Sep 2022 14:21:18 +0800 Subject: [PATCH 2/4] Fix test --- pandas/tests/computation/test_eval.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 275dbc0fff403..d55e1a4573891 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -606,11 +606,11 @@ def test_unary_in_function(self): result = df.eval("x.fillna(-1)") expected = df.x.fillna(-1) - tm.assert_series_equal(result, expected) + tm.assert_series_equal(result, expected, check_names=False) result = df.eval("x.shift(1, fill_value=-1)") expected = df.x.shift(1, fill_value=-1) - tm.assert_series_equal(result, expected) + tm.assert_series_equal(result, expected, check_names=False) @pytest.mark.parametrize( "ex", From 0d7fc3f5b5ff0b013845bc81d3d76120e4501e26 Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Tue, 13 Sep 2022 11:54:00 +0800 Subject: [PATCH 3/4] Add whatsnew --- doc/source/whatsnew/v1.6.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.6.0.rst b/doc/source/whatsnew/v1.6.0.rst index aff950c6933dd..d0977386a7a74 100644 --- a/doc/source/whatsnew/v1.6.0.rst +++ b/doc/source/whatsnew/v1.6.0.rst @@ -145,6 +145,7 @@ Numeric Conversion ^^^^^^^^^^ - Bug in constructing :class:`Series` with ``int64`` dtype from a string list raising instead of casting (:issue:`44923`) +- Bug in :meth:`DataFrame.eval` incorrectly raising an ``AttributeError`` when there are negative values in function call (:issue:`46471`) - Strings From fd45ed44dec02ee32e1560545e387ca99fa9217a Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Wed, 14 Sep 2022 14:12:06 +0800 Subject: [PATCH 4/4] Change name check --- pandas/tests/computation/test_eval.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index d55e1a4573891..dbcf21f778cbf 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -606,11 +606,13 @@ def test_unary_in_function(self): result = df.eval("x.fillna(-1)") expected = df.x.fillna(-1) - tm.assert_series_equal(result, expected, check_names=False) + # column name becomes None if using numexpr + # only check names when the engine is not numexpr + tm.assert_series_equal(result, expected, check_names=not USE_NUMEXPR) result = df.eval("x.shift(1, fill_value=-1)") expected = df.x.shift(1, fill_value=-1) - tm.assert_series_equal(result, expected, check_names=False) + tm.assert_series_equal(result, expected, check_names=not USE_NUMEXPR) @pytest.mark.parametrize( "ex",