diff --git a/doc/source/release.rst b/doc/source/release.rst index 19ad27f33e621..4f0b82684c00c 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -35,6 +35,9 @@ pandas 0.13 **API Changes** +- ``DataFrame`` now supports being the right hand side operand in boolean + operators (:issue:`4331`) + **Experimental Features** **Bug Fixes** diff --git a/doc/source/v0.13.0.txt b/doc/source/v0.13.0.txt index cd28a7907f5ed..0a874a4fc1732 100644 --- a/doc/source/v0.13.0.txt +++ b/doc/source/v0.13.0.txt @@ -9,6 +9,9 @@ enhancements along with a large number of bug fixes. API changes ~~~~~~~~~~~ +- ``DataFrame`` now supports being the right hand side operand in boolean + operators (:issue:`4331`) + Enhancements ~~~~~~~~~~~~ diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 401a7746953cb..f698520aeca3e 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -848,12 +848,17 @@ def __contains__(self, key): # boolean operators __and__ = _arith_method(operator.and_, '__and__', '&') __or__ = _arith_method(operator.or_, '__or__', '|') - __xor__ = _arith_method(operator.xor, '__xor__') + __xor__ = _arith_method(operator.xor, '__xor__', '^') + + __rand__ = _arith_method(lambda x, y: operator.and_(y, x), '__rand__', '&') + __ror__ = _arith_method(lambda x, y: operator.or_(y, x), '__ror__', '|') + __rxor__ = _arith_method(lambda x, y: operator.xor(y, x), '__rxor__', '^') # Python 2 division methods if not py3compat.PY3: __div__ = _arith_method(operator.div, '__div__', '/', - default_axis=None, fill_zeros=np.inf, truediv=False) + default_axis=None, fill_zeros=np.inf, + truediv=False) __rdiv__ = _arith_method(lambda x, y: y / x, '__rdiv__', default_axis=None, fill_zeros=np.inf) @@ -5939,6 +5944,7 @@ def _homogenize(data, index, dtype=None): return homogenized + def _from_nested_dict(data): # TODO: this should be seriously cythonized new_data = OrderedDict() diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index a9df56a498f63..c325e81d74f47 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -4098,11 +4098,30 @@ def test_logical_operators(self): import operator def _check_bin_op(op): - result = op(df1, df2) - expected = DataFrame(op(df1.values, df2.values), index=df1.index, - columns=df1.columns) - self.assert_(result.values.dtype == np.bool_) - assert_frame_equal(result, expected) + import itertools + operands = itertools.product((df1, s1), (df2, s2)) + for opr_set in operands: + lhs, rhs = opr_set + if not (isinstance(lhs, Series) or isinstance(rhs, Series)): + result = op(*opr_set) + expected = DataFrame(op(lhs.values, rhs.values), + index=lhs.index, columns=lhs.columns) + self.assert_(result.values.dtype == np.bool_) + assert_frame_equal(result, expected) + + for df in (df1, df2): + for b in (True, False): + lhs, rhs = b, df + result = op(lhs, rhs) + expected = DataFrame(op(lhs, rhs.values), index=rhs.index, + columns=rhs.columns) + assert_frame_equal(result, expected) + + lhs, rhs = df, b + result = op(lhs, rhs) + expected = DataFrame(op(lhs.values, rhs), index=lhs.index, + columns=lhs.columns) + assert_frame_equal(result, expected) def _check_unary_op(op): result = op(df1) @@ -4130,10 +4149,16 @@ def _check_unary_op(op): df1 = DataFrame(df1) df2 = DataFrame(df2) + s1 = df1.a + s2 = df2.b + + ops = (operator.and_, operator.or_, operator.xor, + lambda x, y: operator.and_(y, x), + lambda x, y: operator.or_(y, x), + lambda x, y: operator.xor(y, x)) - _check_bin_op(operator.and_) - _check_bin_op(operator.or_) - _check_bin_op(operator.xor) + for binop in ops: + _check_bin_op(binop) _check_unary_op(operator.neg)