Skip to content

Logical operations on np.bool_ using pandas.eval raises TypeError #25823

Closed
@james-mchugh

Description

@james-mchugh

Code Sample, a copy-pastable example if possible

# Your code here
import pandas as pd
event = pd.Series({"a": "hello"})
pd.eval("event.str.match('hello').a")  # returns numpy.bool_(True)
pd.eval("event.str.match('hello').a and event.str.match('hello').a")  # raises TypeError

Problem description

When running the last line of the code above, the following error is produced:

TypeError                                 Traceback (most recent call last)
<ipython-input-1-4a1286720430> in <module>
      5 pd.eval("event.str.match('hello').a")  # returns np.bool_(True)
      6 
----> 7 pd.eval("event.str.match('hello').a and event.str.match('hello').a")  # raises TypeError

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/eval.py in eval(expr, parser, engine, truediv, local_dict, global_dict, resolvers, level, target, inplace)
    292 
    293         parsed_expr = Expr(expr, engine=engine, parser=parser, env=env,
--> 294                            truediv=truediv)
    295 
    296         # construct the engine and evaluate the parsed expression

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in __init__(self, expr, engine, parser, env, truediv, level)
    747         self.env.scope['truediv'] = truediv
    748         self._visitor = _parsers[parser](self.env, self.engine, self.parser)
--> 749         self.terms = self.parse()
    750 
    751     @property

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in parse(self)
    764     def parse(self):
    765         """Parse an expression"""
--> 766         return self._visitor.visit(self.expr)
    767 
    768     @property

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit(self, node, **kwargs)
    329         method = 'visit_' + node.__class__.__name__
    330         visitor = getattr(self, method)
--> 331         return visitor(node, **kwargs)
    332 
    333     def visit_Module(self, node, **kwargs):

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit_Module(self, node, **kwargs)
    335             raise SyntaxError('only a single expression is allowed')
    336         expr = node.body[0]
--> 337         return self.visit(expr, **kwargs)
    338 
    339     def visit_Expr(self, node, **kwargs):

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit(self, node, **kwargs)
    329         method = 'visit_' + node.__class__.__name__
    330         visitor = getattr(self, method)
--> 331         return visitor(node, **kwargs)
    332 
    333     def visit_Module(self, node, **kwargs):

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit_Expr(self, node, **kwargs)
    338 
    339     def visit_Expr(self, node, **kwargs):
--> 340         return self.visit(node.value, **kwargs)
    341 
    342     def _rewrite_membership_op(self, node, left, right):

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit(self, node, **kwargs)
    329         method = 'visit_' + node.__class__.__name__
    330         visitor = getattr(self, method)
--> 331         return visitor(node, **kwargs)
    332 
    333     def visit_Module(self, node, **kwargs):

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visit_BoolOp(self, node, **kwargs)
    691 
    692         operands = node.values
--> 693         return reduce(visitor, operands)
    694 
    695 

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in visitor(x, y)
    688             op, op_class, lhs, rhs = self._maybe_transform_eq_ne(
    689                 node, lhs, rhs)
--> 690             return self._maybe_evaluate_binop(op, node.op, lhs, rhs)
    691 
    692         operands = node.values

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/expr.py in _maybe_evaluate_binop(self, op, op_class, lhs, rhs, eval_in_python, maybe_eval_in_python)
    404                               maybe_eval_in_python=('==', '!=', '<', '>',
    405                                                     '<=', '>=')):
--> 406         res = op(lhs, rhs)
    407 
    408         if res.has_invalid_return_type:

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/ops.py in __init__(self, op, lhs, rhs, **kwargs)
    335         self.rhs = rhs
    336 
--> 337         self._disallow_scalar_only_bool_ops()
    338 
    339         self.convert_values()

~/anaconda3/lib/python3.7/site-packages/pandas/core/computation/ops.py in _disallow_scalar_only_bool_ops(self)
    440         if ((self.lhs.is_scalar or self.rhs.is_scalar) and
    441             self.op in _bool_ops_dict and
--> 442             (not (issubclass(self.rhs.return_type, (bool, np.bool_)) and
    443                   issubclass(self.lhs.return_type, (bool, np.bool_))))):
    444             raise NotImplementedError("cannot evaluate scalar only bool ops")

TypeError: issubclass() arg 1 must be a class

The expected output of this operation is a boolean indicating numpy.bool_(True) or numpy.bool_(False). This is the output when running the following code.

event.str.match("hello").a and event.str.match("hello").a  # outputs numpy.bool_(True)

Also, when using Python's builtin booleans with pd.eval, the behavior is as expected.

pd.eval("True and True")  # returns True

I think that this behavior should be used when running boolean operations on numpy.bool_ types.

Expected Output

Expected output of pd.eval("event.str.match('hello').a and event.str.match('hello').a"):
True

Output of pd.show_versions()

[paste the output of pd.show_versions() here below this line]

INSTALLED VERSIONS

commit: None
python: 3.7.2.final.0
python-bits: 64
OS: Linux
OS-release: 3.10.0-957.5.1.el7.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.24.2
pytest: 4.2.0
pip: 19.0.1
setuptools: 40.7.3
Cython: 0.29.4
numpy: 1.15.4
scipy: 1.1.0
pyarrow: None
xarray: None
IPython: 7.2.0
sphinx: 1.8.4
patsy: 0.5.0
dateutil: 2.7.5
pytz: 2018.9
blosc: None
bottleneck: 1.2.1
tables: 3.4.4
numexpr: 2.6.8
feather: None
matplotlib: 2.2.3
openpyxl: 2.5.14
xlrd: 1.2.0
xlwt: 1.3.0
xlsxwriter: 1.1.2
lxml.etree: 4.3.0
bs4: 4.7.1
html5lib: 1.0.1
sqlalchemy: 1.2.17
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
gcsfs: None

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions