diff --git a/doc/source/whatsnew/v1.4.4.rst b/doc/source/whatsnew/v1.4.4.rst index 57b8fdee5888a..1e7ed256c05ef 100644 --- a/doc/source/whatsnew/v1.4.4.rst +++ b/doc/source/whatsnew/v1.4.4.rst @@ -19,6 +19,7 @@ Fixed regressions - Fixed regression in :meth:`DataFrame.loc` not updating the cache correctly after values were set (:issue:`47867`) - Fixed regression in :meth:`DataFrame.loc` not aligning index in some cases when setting a :class:`DataFrame` (:issue:`47578`) - Fixed regression in setting ``None`` or non-string value into a ``string``-dtype Series using a mask (:issue:`47628`) +- Fixed regression in :meth:`DataFrame.eval` creating a copy when updating inplace (:issue:`47449`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py index fa0ef46b850d1..0a9bb6a4bb6fa 100644 --- a/pandas/core/computation/eval.py +++ b/pandas/core/computation/eval.py @@ -19,6 +19,7 @@ ) from pandas.core.computation.parsing import tokenize_string from pandas.core.computation.scope import ensure_scope +from pandas.core.generic import NDFrame from pandas.io.formats.printing import pprint_thing @@ -387,7 +388,10 @@ def eval( try: with warnings.catch_warnings(record=True): # TODO: Filter the warnings we actually care about here. - target[assigner] = ret + if inplace and isinstance(target, NDFrame): + target.loc[:, assigner] = ret + else: + target[assigner] = ret except (TypeError, IndexError) as err: raise ValueError("Cannot assign expression output to target") from err diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 672a87edbad66..b3b80e8e5db65 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1912,6 +1912,26 @@ def test_eval_no_support_column_name(request, column): tm.assert_frame_equal(result, expected) +@td.skip_array_manager_not_yet_implemented +def test_set_inplace(using_copy_on_write): + # https://github.com/pandas-dev/pandas/issues/47449 + # Ensure we don't only update the DataFrame inplace, but also the actual + # column values, such that references to this column also get updated + df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) + result_view = df[:] + ser = df["A"] + df.eval("A = B + C", inplace=True) + expected = DataFrame({"A": [11, 13, 15], "B": [4, 5, 6], "C": [7, 8, 9]}) + tm.assert_frame_equal(df, expected) + if not using_copy_on_write: + tm.assert_series_equal(ser, expected["A"]) + tm.assert_series_equal(result_view["A"], expected["A"]) + else: + expected = Series([1, 2, 3], name="A") + tm.assert_series_equal(ser, expected) + tm.assert_series_equal(result_view["A"], expected) + + class TestValidate: @pytest.mark.parametrize("value", [1, "True", [1, 2, 3], 5.0]) def test_validate_bool_args(self, value):