diff --git a/doc/source/release.rst b/doc/source/release.rst index 8bf6a8d7b9488..7188851214f7f 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -156,6 +156,8 @@ API Changes - ``to_excel`` now converts ``np.inf`` into a string representation, customizable by the ``inf_rep`` keyword argument (Excel has no native inf representation) (:issue:`6782`) +- Arithmetic ops are now disallowed when passed two bool dtype Series or + DataFrames (:issue:`6762`). Deprecations ~~~~~~~~~~~~ @@ -307,6 +309,9 @@ Bug Fixes - Bug in ``DataFrame.replace()`` where regex metacharacters were being treated as regexs even when ``regex=False`` (:issue:`6777`). - Bug in timedelta ops on 32-bit platforms (:issue:`6808`) +- Bug in setting a tz-aware index directly via ``.index`` (:issue:`6785`) +- Bug in expressions.py where numexpr would try to evaluate arithmetic ops + (:issue:`6762`). pandas 0.13.1 ------------- diff --git a/pandas/computation/expressions.py b/pandas/computation/expressions.py index b379da9cd38bc..128aa5bf2b511 100644 --- a/pandas/computation/expressions.py +++ b/pandas/computation/expressions.py @@ -154,6 +154,20 @@ def _where_numexpr(cond, a, b, raise_on_error=False): set_use_numexpr(True) +def _has_bool_dtype(x): + try: + return x.dtype == bool + except AttributeError: + return 'bool' in x.blocks + + +def _bool_arith_check(op_str, a, b, not_allowed=frozenset(('+', '*', '-', '/', + '//', '**'))): + if op_str in not_allowed and _has_bool_dtype(a) and _has_bool_dtype(b): + raise NotImplementedError("operator %r not implemented for bool " + "dtypes" % op_str) + + def evaluate(op, op_str, a, b, raise_on_error=False, use_numexpr=True, **eval_kwargs): """ evaluate and return the expression of the op on a and b @@ -170,7 +184,7 @@ def evaluate(op, op_str, a, b, raise_on_error=False, use_numexpr=True, return the results use_numexpr : whether to try to use numexpr (default True) """ - + _bool_arith_check(op_str, a, b) if use_numexpr: return _evaluate(op, op_str, a, b, raise_on_error=raise_on_error, **eval_kwargs) diff --git a/pandas/tests/test_expressions.py b/pandas/tests/test_expressions.py index 2b539b3386226..fdea275b7e040 100644 --- a/pandas/tests/test_expressions.py +++ b/pandas/tests/test_expressions.py @@ -2,6 +2,7 @@ # pylint: disable-msg=W0612,E1101 import nose +import re from numpy.random import randn @@ -339,6 +340,24 @@ def testit(): expr.set_numexpr_threads() testit() + def test_bool_ops_raise_on_arithmetic(self): + df = DataFrame({'a': np.random.rand(10) > 0.5, + 'b': np.random.rand(10) > 0.5}) + names = 'add', 'mul', 'sub', 'div', 'truediv', 'floordiv', 'pow' + ops = '+', '*', '-', '/', '/', '//', '**' + msg = 'operator %r not implemented for bool dtypes' + for op, name in zip(ops, names): + if not compat.PY3 or name != 'div': + f = getattr(operator, name) + err_msg = re.escape(msg % op) + + with tm.assertRaisesRegexp(NotImplementedError, err_msg): + f(df, df) + + with tm.assertRaisesRegexp(NotImplementedError, err_msg): + f(df.a, df.b) + + if __name__ == '__main__': import nose nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'], diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 087e094ffbcb8..877e3839ee11f 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -4761,13 +4761,13 @@ def _check_unary_op(op): _check_unary_op(operator.neg) def test_logical_typeerror(self): - if compat.PY3: - pass - else: + if not compat.PY3: self.assertRaises(TypeError, self.frame.__eq__, 'foo') self.assertRaises(TypeError, self.frame.__lt__, 'foo') self.assertRaises(TypeError, self.frame.__gt__, 'foo') self.assertRaises(TypeError, self.frame.__ne__, 'foo') + else: + raise nose.SkipTest('test_logical_typeerror not tested on PY3') def test_constructor_lists_to_object_dtype(self): # from #1074