Skip to content

ERR: more informative error message on bool arith op failures #7015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 14, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ API Changes
- Added ``factorize`` functions to ``Index`` and ``Series`` to get indexer and unique values (:issue:`7090`)
- :meth:`DataFrame.describe` on a DataFrame with a mix of Timestamp and string like objects
returns a different Index (:issue:`7088`). Previously the index was unintentionally sorted.
- arithmetic operations with **only** ``bool`` dtypes now raise an error
(:issue:`7011`, :issue:`6762`, :issue:`7015`)

Deprecations
~~~~~~~~~~~~
Expand Down
12 changes: 12 additions & 0 deletions doc/source/v0.14.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ Display Changes
length of the series (:issue:`7101`)
- Fixed a bug in the HTML repr of a truncated Series or DataFrame not showing the class name with the
`large_repr` set to 'info' (:issue:`7105`)
- arithmetic operations with **only** ``bool`` dtypes now raise an error
(:issue:`7011`, :issue:`6762`, :issue:`7015`)

.. code-block:: python

x = pd.Series(np.random.rand(10) > 0.5)
y = True
x * y

# this now raises for arith ops like ``+``, ``*``, etc.
NotImplementedError: operator '*' not implemented for bool dtypes


.. _whatsnew_0140.groupby:

Expand Down
5 changes: 4 additions & 1 deletion pandas/computation/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ def _has_bool_dtype(x):
try:
return x.dtype == bool
except AttributeError:
return 'bool' in x.blocks
try:
return 'bool' in x.blocks
except AttributeError:
return isinstance(x, (bool, np.bool_))


def _bool_arith_check(op_str, a, b, not_allowed=frozenset(('+', '*', '-', '/',
Expand Down
34 changes: 17 additions & 17 deletions pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,25 @@ def names(x):
default_axis=default_axis, fill_zeros=np.inf),
# Causes a floating point exception in the tests when numexpr
# enabled, so for now no speedup
mod=arith_method(operator.mod, names('mod'), default_axis=default_axis,
fill_zeros=np.nan),
mod=arith_method(operator.mod, names('mod'), None,
default_axis=default_axis, fill_zeros=np.nan),
pow=arith_method(operator.pow, names('pow'), op('**'),
default_axis=default_axis),
# not entirely sure why this is necessary, but previously was included
# so it's here to maintain compatibility
rmul=arith_method(operator.mul, names('rmul'),
rmul=arith_method(operator.mul, names('rmul'), op('*'),
default_axis=default_axis),
rsub=arith_method(lambda x, y: y - x, names('rsub'),
rsub=arith_method(lambda x, y: y - x, names('rsub'), op('-'),
default_axis=default_axis),
rtruediv=arith_method(lambda x, y: operator.truediv(y, x),
names('rtruediv'), truediv=True,
names('rtruediv'), op('/'), truediv=True,
fill_zeros=np.inf, default_axis=default_axis),
rfloordiv=arith_method(lambda x, y: operator.floordiv(y, x),
names('rfloordiv'), default_axis=default_axis,
fill_zeros=np.inf),
rpow=arith_method(lambda x, y: y ** x, names('rpow'),
names('rfloordiv'), op('//'),
default_axis=default_axis, fill_zeros=np.inf),
rpow=arith_method(lambda x, y: y ** x, names('rpow'), op('**'),
default_axis=default_axis),
rmod=arith_method(lambda x, y: y % x, names('rmod'),
rmod=arith_method(lambda x, y: y % x, names('rmod'), op('%'),
default_axis=default_axis),
)
new_methods['div'] = new_methods['truediv']
Expand All @@ -100,11 +100,11 @@ def names(x):
and_=bool_method(operator.and_, names('and_'), op('&')),
or_=bool_method(operator.or_, names('or_'), op('|')),
# For some reason ``^`` wasn't used in original.
xor=bool_method(operator.xor, names('xor')),
xor=bool_method(operator.xor, names('xor'), op('^')),
rand_=bool_method(lambda x, y: operator.and_(y, x),
names('rand_')),
ror_=bool_method(lambda x, y: operator.or_(y, x), names('ror_')),
rxor=bool_method(lambda x, y: operator.xor(y, x), names('rxor'))
names('rand_'), op('&')),
ror_=bool_method(lambda x, y: operator.or_(y, x), names('ror_'), op('|')),
rxor=bool_method(lambda x, y: operator.xor(y, x), names('rxor'), op('^'))
))

new_methods = dict((names(k), v) for k, v in new_methods.items())
Expand Down Expand Up @@ -431,7 +431,7 @@ def maybe_convert_for_time_op(cls, left, right, name):
return cls(left, right, name)


def _arith_method_SERIES(op, name, str_rep=None, fill_zeros=None,
def _arith_method_SERIES(op, name, str_rep, fill_zeros=None,
default_axis=None, **eval_kwargs):
"""
Wrapper function for Series arithmetic operations, to avoid
Expand Down Expand Up @@ -506,7 +506,7 @@ def wrapper(left, right, name=name):
return wrapper


def _comp_method_SERIES(op, name, str_rep=None, masker=False):
def _comp_method_SERIES(op, name, str_rep, masker=False):
"""
Wrapper function for Series arithmetic operations, to avoid
code duplication.
Expand Down Expand Up @@ -578,7 +578,7 @@ def wrapper(self, other):
return wrapper


def _bool_method_SERIES(op, name, str_rep=None):
def _bool_method_SERIES(op, name, str_rep):
"""
Wrapper function for Series arithmetic operations, to avoid
code duplication.
Expand Down Expand Up @@ -647,7 +647,7 @@ def _radd_compat(left, right):
return output


def _flex_method_SERIES(op, name, str_rep=None, default_axis=None,
def _flex_method_SERIES(op, name, str_rep, default_axis=None,
fill_zeros=None, **eval_kwargs):
doc = """
Binary operator %s with support to substitute a fill_value for missing data
Expand Down
12 changes: 12 additions & 0 deletions pandas/tests/test_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,18 @@ def test_bool_ops_raise_on_arithmetic(self):
with tm.assertRaisesRegexp(NotImplementedError, err_msg):
f(df.a, df.b)

with tm.assertRaisesRegexp(NotImplementedError, err_msg):
f(df.a, True)

with tm.assertRaisesRegexp(NotImplementedError, err_msg):
f(False, df.a)

with tm.assertRaisesRegexp(TypeError, err_msg):
f(False, df)

with tm.assertRaisesRegexp(TypeError, err_msg):
f(df, True)


if __name__ == '__main__':
import nose
Expand Down