Skip to content

Commit

Permalink
BUG: wrap all supported inplace methods to avoid making a copy (panda…
Browse files Browse the repository at this point in the history
  • Loading branch information
dkamm authored and alanbato committed Nov 10, 2017
1 parent e916418 commit 2869edb
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ PyPy

Other
^^^^^
- Bug where some inplace operators were not being wrapped and produced a copy when invoked (:issue:`12962`)
- Bug in :func:`eval` where the ``inplace`` parameter was being incorrectly handled (:issue:`16732`)
<<<<<<< HEAD
- The ``Series`` constructor with no arguments would have an index like ``Index([], dtype='object')`` instead of ``RangeIndex(start=0, stop=0, step=1)``
Expand Down
15 changes: 12 additions & 3 deletions pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,10 @@ def add_special_arithmetic_methods(cls, arith_method=None,
arith_method : function (optional)
factory for special arithmetic methods, with op string:
f(op, name, str_rep, default_axis=None, fill_zeros=None, **eval_kwargs)
comp_method : function, optional,
comp_method : function (optional)
factory for rich comparison - signature: f(op, name, str_rep)
bool_method : function (optional)
factory for boolean methods - signature: f(op, name, str_rep)
use_numexpr : bool, default True
whether to accelerate with numexpr, defaults to True
force : bool, default False
Expand Down Expand Up @@ -234,9 +236,16 @@ def f(self, other):
__isub__=_wrap_inplace_method(new_methods["__sub__"]),
__imul__=_wrap_inplace_method(new_methods["__mul__"]),
__itruediv__=_wrap_inplace_method(new_methods["__truediv__"]),
__ipow__=_wrap_inplace_method(new_methods["__pow__"]), ))
__ifloordiv__=_wrap_inplace_method(new_methods["__floordiv__"]),
__imod__=_wrap_inplace_method(new_methods["__mod__"]),
__ipow__=_wrap_inplace_method(new_methods["__pow__"])))
if not compat.PY3:
new_methods["__idiv__"] = new_methods["__div__"]
new_methods["__idiv__"] = _wrap_inplace_method(new_methods["__div__"])
if bool_method:
new_methods.update(
dict(__iand__=_wrap_inplace_method(new_methods["__and__"]),
__ior__=_wrap_inplace_method(new_methods["__or__"]),
__ixor__=_wrap_inplace_method(new_methods["__xor__"])))

add_methods(cls, new_methods=new_methods, force=force, select=select,
exclude=exclude)
Expand Down
27 changes: 27 additions & 0 deletions pandas/tests/frame/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,33 @@ def test_inplace_ops_identity(self):
assert_frame_equal(df2, expected)
assert df._data is df2._data

@pytest.mark.parametrize('op', ['add', 'and', 'div', 'floordiv', 'mod',
'mul', 'or', 'pow', 'sub', 'truediv',
'xor'])
def test_inplace_ops_identity2(self, op):

if compat.PY3 and op == 'div':
return

df = DataFrame({'a': [1., 2., 3.],
'b': [1, 2, 3]})

operand = 2
if op in ('and', 'or', 'xor'):
# cannot use floats for boolean ops
df['a'] = [True, False, True]

df_copy = df.copy()
iop = '__i{}__'.format(op)
op = '__{}__'.format(op)

# no id change and value is correct
getattr(df, iop)(operand)
expected = getattr(df_copy, op)(operand)
assert_frame_equal(df, expected)
expected = id(df)
assert id(df) == expected

def test_alignment_non_pandas(self):
index = ['A', 'B', 'C']
columns = ['X', 'Y', 'Z']
Expand Down

0 comments on commit 2869edb

Please sign in to comment.