Skip to content

Commit 233ebdf

Browse files
phoflCloseChoicejorisvandenbossche
authored
Backport PR #47550 on branch 1.4.x (REGR: fix eval with inplace=True to correctly update column values inplace) (#48151)
* REGR: fix eval with inplace=True to correctly update column values inplace (#47550) * fix pre-commit issues * fix linting errors * add inplace argument to isetitem and use in eval * changes due to PR discussions * fix issues * update eval * update whatsnew * add PR suggestions * update imports in eval.py * check inplace and use NDFrame + small update to test * update test to use using_copy_on_write * skip test for array manager Co-authored-by: Joris Van den Bossche <[email protected]> * Remove copy on write fixture Co-authored-by: Tobias Pitters <[email protected]> Co-authored-by: Joris Van den Bossche <[email protected]>
1 parent 8b58762 commit 233ebdf

File tree

3 files changed

+21
-1
lines changed

3 files changed

+21
-1
lines changed

doc/source/whatsnew/v1.4.4.rst

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Fixed regressions
1919
- Fixed regression in :meth:`DataFrame.loc` not updating the cache correctly after values were set (:issue:`47867`)
2020
- Fixed regression in :meth:`DataFrame.loc` not aligning index in some cases when setting a :class:`DataFrame` (:issue:`47578`)
2121
- Fixed regression in setting ``None`` or non-string value into a ``string``-dtype Series using a mask (:issue:`47628`)
22+
- Fixed regression in :meth:`DataFrame.eval` creating a copy when updating inplace (:issue:`47449`)
2223
-
2324

2425
.. ---------------------------------------------------------------------------

pandas/core/computation/eval.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pandas.core.computation.ops import BinOp
1919
from pandas.core.computation.parsing import tokenize_string
2020
from pandas.core.computation.scope import ensure_scope
21+
from pandas.core.generic import NDFrame
2122

2223
from pandas.io.formats.printing import pprint_thing
2324

@@ -384,7 +385,10 @@ def eval(
384385
try:
385386
with warnings.catch_warnings(record=True):
386387
# TODO: Filter the warnings we actually care about here.
387-
target[assigner] = ret
388+
if inplace and isinstance(target, NDFrame):
389+
target.loc[:, assigner] = ret
390+
else:
391+
target[assigner] = ret
388392
except (TypeError, IndexError) as err:
389393
raise ValueError("Cannot assign expression output to target") from err
390394

pandas/tests/computation/test_eval.py

+15
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,21 @@ def test_negate_lt_eq_le(engine, parser):
19661966
tm.assert_frame_equal(result, expected)
19671967

19681968

1969+
@td.skip_array_manager_not_yet_implemented
1970+
def test_set_inplace():
1971+
# https://github.com/pandas-dev/pandas/issues/47449
1972+
# Ensure we don't only update the DataFrame inplace, but also the actual
1973+
# column values, such that references to this column also get updated
1974+
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]})
1975+
result_view = df[:]
1976+
ser = df["A"]
1977+
df.eval("A = B + C", inplace=True)
1978+
expected = DataFrame({"A": [11, 13, 15], "B": [4, 5, 6], "C": [7, 8, 9]})
1979+
tm.assert_frame_equal(df, expected)
1980+
tm.assert_series_equal(ser, expected["A"])
1981+
tm.assert_series_equal(result_view["A"], expected["A"])
1982+
1983+
19691984
class TestValidate:
19701985
@pytest.mark.parametrize("value", [1, "True", [1, 2, 3], 5.0])
19711986
def test_validate_bool_args(self, value):

0 commit comments

Comments
 (0)