diff --git a/doc/source/whatsnew/v1.6.0.rst b/doc/source/whatsnew/v1.6.0.rst index a7c9a7eb88221..500f63bd0a51f 100644 --- a/doc/source/whatsnew/v1.6.0.rst +++ b/doc/source/whatsnew/v1.6.0.rst @@ -148,6 +148,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 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..dbcf21f778cbf 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -600,6 +600,20 @@ 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) + # 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=not USE_NUMEXPR) + @pytest.mark.parametrize( "ex", (