From 0351a370c5708317399f40233d4b4d1761c6f5bd Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 13:46:05 -0800 Subject: [PATCH 1/7] BUG: default+input resolvers in df.eval, GH34966 Allow default resolvers to be used with input resolvers in dataframe.eval. Resolves GH34966 --- pandas/core/frame.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 252534a0cb790..911cb3594343e 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4221,15 +4221,13 @@ def eval(self, expr: str, inplace: bool = False, **kwargs): from pandas.core.computation.eval import eval as _eval inplace = validate_bool_kwarg(inplace, "inplace") - resolvers = kwargs.pop("resolvers", None) kwargs["level"] = kwargs.pop("level", 0) + 1 - if resolvers is None: - index_resolvers = self._get_index_resolvers() - column_resolvers = self._get_cleaned_column_resolvers() - resolvers = column_resolvers, index_resolvers + index_resolvers = self._get_index_resolvers() + column_resolvers = self._get_cleaned_column_resolvers() + resolvers = column_resolvers, index_resolvers if "target" not in kwargs: kwargs["target"] = self - kwargs["resolvers"] = kwargs.get("resolvers", ()) + tuple(resolvers) + kwargs["resolvers"] = tuple(kwargs.get("resolvers", ())) + resolvers return _eval(expr, inplace=inplace, **kwargs) From fd51742e0c4a151a2052c02951f0d35bbc956f7b Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 13:58:09 -0800 Subject: [PATCH 2/7] Add whatsnew entry --- doc/source/whatsnew/v1.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 3924191bebcfd..71bf2d823e36f 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -977,6 +977,7 @@ Other - Bug in :meth:`DataFrame.diff` when passing a NumPy integer object instead of an ``int`` object (:issue:`44572`) - Bug in :meth:`Series.replace` raising ``ValueError`` when using ``regex=True`` with a :class:`Series` containing ``np.nan`` values (:issue:`43344`) - Bug in :meth:`DataFrame.to_records` where an incorrect ``n`` was used when missing names were replaced by ``level_n`` (:issue:`44818`) +- Bug in :meth:`DataFrame.eval` where ``resolvers`` argument was overriding the default resolvers .. ***DO NOT USE THIS SECTION*** From d8d1496c6a9266e909e3d50a4b98a9924a72da18 Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 14:46:24 -0800 Subject: [PATCH 3/7] TST: dataframe.eval input resolvers test --- pandas/tests/frame/test_query_eval.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 331c21de8e4bd..9eda509aa42d5 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -164,6 +164,17 @@ def test_eval_resolvers_as_list(self): dict2 = {"b": 2} assert df.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] assert pd.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] + + def test_eval_resolvers_combined(self): + # GH 34966 + df = DataFrame(np.random.randn(10,2), columns=list("ab")) + dict1 = {"c": 2} + + # Both input and default index/column resolvers should be usable + result = df.eval("a + b * c", resolvers = [dict1]) + + expected = df["a"] + df["b"] * dict1["c"] + tm.assert_frame_equal(result, expected) def test_eval_object_dtype_binop(self): # GH#24883 From 17d70a8bd262ce3a79f063d87b8a09f71843e6a1 Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 14:58:11 -0800 Subject: [PATCH 4/7] CLN: black formatting and assert_series_equal not frame --- pandas/tests/frame/test_query_eval.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 9eda509aa42d5..abbdd88475547 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -164,17 +164,17 @@ def test_eval_resolvers_as_list(self): dict2 = {"b": 2} assert df.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] assert pd.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] - - def test_eval_resolvers_combined(self): + + def test_eval_resolvers_combined(self): # GH 34966 - df = DataFrame(np.random.randn(10,2), columns=list("ab")) + df = DataFrame(np.random.randn(10, 2), columns=list("ab")) dict1 = {"c": 2} - + # Both input and default index/column resolvers should be usable - result = df.eval("a + b * c", resolvers = [dict1]) - - expected = df["a"] + df["b"] * dict1["c"] - tm.assert_frame_equal(result, expected) + result = df.eval("a + b * c", resolvers=[dict1]) + + expected = df[["a"]] + df[["b"]] * dict1["c"] + tm.assert_series_equal(result, expected) def test_eval_object_dtype_binop(self): # GH#24883 From 13dc642b5a38f1dffb08e2f25e6ace8dbe40e683 Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 15:01:59 -0800 Subject: [PATCH 5/7] Fix typo --- pandas/tests/frame/test_query_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index abbdd88475547..dca37f6c9676b 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -165,7 +165,7 @@ def test_eval_resolvers_as_list(self): assert df.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] assert pd.eval("a + b", resolvers=[dict1, dict2]) == dict1["a"] + dict2["b"] - def test_eval_resolvers_combined(self): + def test_eval_resolvers_combined(self): # GH 34966 df = DataFrame(np.random.randn(10, 2), columns=list("ab")) dict1 = {"c": 2} From f3dd3fd67f24513dca76e6887523008b691390f3 Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Thu, 30 Dec 2021 15:51:18 -0800 Subject: [PATCH 6/7] TST: Minor fix to test_eval_resolvers_combined --- pandas/tests/frame/test_query_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index dca37f6c9676b..558ba0424e481 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -173,7 +173,7 @@ def test_eval_resolvers_combined(self): # Both input and default index/column resolvers should be usable result = df.eval("a + b * c", resolvers=[dict1]) - expected = df[["a"]] + df[["b"]] * dict1["c"] + expected = df["a"] + df["b"] * dict1["c"] tm.assert_series_equal(result, expected) def test_eval_object_dtype_binop(self): From c276d971ab9adc6ef3f3cec3ac208f42dfa4a43c Mon Sep 17 00:00:00 2001 From: bubblingoak Date: Mon, 3 Jan 2022 15:30:42 -0800 Subject: [PATCH 7/7] DOC: Add issue number to whatsnew entry --- doc/source/whatsnew/v1.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 71bf2d823e36f..178263aeb3ccb 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -977,7 +977,7 @@ Other - Bug in :meth:`DataFrame.diff` when passing a NumPy integer object instead of an ``int`` object (:issue:`44572`) - Bug in :meth:`Series.replace` raising ``ValueError`` when using ``regex=True`` with a :class:`Series` containing ``np.nan`` values (:issue:`43344`) - Bug in :meth:`DataFrame.to_records` where an incorrect ``n`` was used when missing names were replaced by ``level_n`` (:issue:`44818`) -- Bug in :meth:`DataFrame.eval` where ``resolvers`` argument was overriding the default resolvers +- Bug in :meth:`DataFrame.eval` where ``resolvers`` argument was overriding the default resolvers (:issue:`34966`) .. ***DO NOT USE THIS SECTION***