Repository: pandas-dev/pandas

Issue:
Inf in query / eval
In `df.eval()` or `df.query()`, is there any way to run comparisons against `np.Inf`?

When I do:

`temp_df.eval('my_col < Inf')` or
`temp_df.eval('my_col < np.Inf')` or

I get `UndefinedVariableError`



Discussion:
Just needs to be added to the global constants. 

Thanks @cpcloud What do you mean? Is this something specific to `numexpr` or `Pandas`? 

I can do this in an hour or so if you look in computation/scope.py and want to take a crack at it before then please do. 


Resolving patch:
diff --git a/pandas/computation/ops.py b/pandas/computation/ops.py
--- a/pandas/computation/ops.py
+++ b/pandas/computation/ops.py
@@ -14,6 +14,7 @@
 import pandas.core.common as com
 from pandas.core.base import StringMixin
 from pandas.computation.common import _ensure_decoded, _result_type_many
+from pandas.computation.scope import _DEFAULT_GLOBALS
 
 
 _reductions = 'sum', 'prod'
@@ -48,7 +49,9 @@ def __init__(self, name, env, side=None, encoding=None):
         self._name = name
         self.env = env
         self.side = side
-        self.is_local = text_type(name).startswith(_LOCAL_TAG)
+        tname = text_type(name)
+        self.is_local = (tname.startswith(_LOCAL_TAG) or
+                         tname in _DEFAULT_GLOBALS)
         self._value = self._resolve_name()
         self.encoding = encoding
 
diff --git a/pandas/computation/scope.py b/pandas/computation/scope.py
--- a/pandas/computation/scope.py
+++ b/pandas/computation/scope.py
@@ -2,18 +2,18 @@
 """
 
 import sys
-import operator
 import struct
 import inspect
 import datetime
 import itertools
 import pprint
 
+import numpy as np
+
 import pandas as pd
 from pandas.compat import DeepChainMap, map, StringIO
-from pandas.core import common as com
 from pandas.core.base import StringMixin
-from pandas.computation.ops import UndefinedVariableError, _LOCAL_TAG
+import pandas.computation as compu
 
 
 def _ensure_scope(level, global_dict=None, local_dict=None, resolvers=(),
@@ -45,14 +45,15 @@ def _raw_hex_id(obj):
     return ''.join(map(_replacer, packed))
 
 
-
 _DEFAULT_GLOBALS = {
     'Timestamp': pd.lib.Timestamp,
     'datetime': datetime.datetime,
     'True': True,
     'False': False,
     'list': list,
-    'tuple': tuple
+    'tuple': tuple,
+    'inf': np.inf,
+    'Inf': np.inf,
 }
 
 
@@ -186,7 +187,7 @@ def resolve(self, key, is_local):
                 # e.g., df[df > 0]
                 return self.temps[key]
             except KeyError:
-                raise UndefinedVariableError(key, is_local)
+                raise compu.ops.UndefinedVariableError(key, is_local)
 
     def swapkey(self, old_key, new_key, new_value=None):
         """Replace a variable name, with a potentially new value.


Test patch:
diff --git a/pandas/computation/tests/test_eval.py b/pandas/computation/tests/test_eval.py
--- a/pandas/computation/tests/test_eval.py
+++ b/pandas/computation/tests/test_eval.py
@@ -1,11 +1,10 @@
 #!/usr/bin/env python
 
-import functools
 from itertools import product
 from distutils.version import LooseVersion
 
 import nose
-from nose.tools import assert_raises, assert_true, assert_false, assert_equal
+from nose.tools import assert_raises
 
 from numpy.random import randn, rand, randint
 import numpy as np
@@ -887,7 +886,7 @@ def check_complex_series_frame_alignment(self, engine, parser):
                 expected = expected2 + df
 
             res = pd.eval('df2 + s + df', engine=engine, parser=parser)
-            assert_equal(res.shape, expected.shape)
+            tm.assert_equal(res.shape, expected.shape)
             assert_frame_equal(res, expected)
 
     @slow
@@ -930,13 +929,13 @@ def check_performance_warning_for_poor_alignment(self, engine, parser):
             pd.eval('df + s', engine=engine, parser=parser)
 
             if not is_python_engine:
-                assert_equal(len(w), 1)
+                tm.assert_equal(len(w), 1)
                 msg = str(w[0].message)
                 expected = ("Alignment difference on axis {0} is larger"
                             " than an order of magnitude on term {1!r}, "
                             "by more than {2:.4g}; performance may suffer"
                             "".format(1, 'df', np.log10(s.size - df.shape[1])))
-                assert_equal(msg, expected)
+                tm.assert_equal(msg, expected)
 
     def test_performance_warning_for_poor_alignment(self):
         for engine, parser in ENGINES_PARSERS:
@@ -982,17 +981,17 @@ def test_simple_arith_ops(self):
             else:
                 expec = _eval_single_bin(1, op, 1, self.engine)
                 x = self.eval(ex, engine=self.engine, parser=self.parser)
-                assert_equal(x, expec)
+                tm.assert_equal(x, expec)
 
                 expec = _eval_single_bin(x, op, 1, self.engine)
                 y = self.eval(ex2, local_dict={'x': x}, engine=self.engine,
                               parser=self.parser)
-                assert_equal(y, expec)
+                tm.assert_equal(y, expec)
 
                 expec = _eval_single_bin(1, op, x + 1, self.engine)
                 y = self.eval(ex3, local_dict={'x': x},
                               engine=self.engine, parser=self.parser)
-                assert_equal(y, expec)
+                tm.assert_equal(y, expec)
 
     def test_simple_bool_ops(self):
         for op, lhs, rhs in product(expr._bool_ops_syms, (True, False),
@@ -1024,7 +1023,7 @@ def test_4d_ndarray_fails(self):
 
     def test_constant(self):
         x = self.eval('1')
-        assert_equal(x, 1)
+        tm.assert_equal(x, 1)
 
     def test_single_variable(self):
         df = DataFrame(randn(10, 2))
@@ -1379,7 +1378,7 @@ def check_no_new_locals(self, engine, parser):
         pd.eval('x + 1', local_dict=lcls, engine=engine, parser=parser)
         lcls2 = locals().copy()
         lcls2.pop('lcls')
-        assert_equal(lcls, lcls2)
+        tm.assert_equal(lcls, lcls2)
 
     def test_no_new_locals(self):
         for engine, parser in product(_engines, expr._parsers):
@@ -1391,7 +1390,7 @@ def check_no_new_globals(self, engine, parser):
         gbls = globals().copy()
         pd.eval('x + 1', engine=engine, parser=parser)
         gbls2 = globals().copy()
-        assert_equal(gbls, gbls2)
+        tm.assert_equal(gbls, gbls2)
 
     def test_no_new_globals(self):
         for engine, parser in product(_engines, expr._parsers):
@@ -1556,7 +1555,7 @@ def check_bool_ops_fails_on_scalars(gen, lhs, cmp, rhs, engine, parser):
 def test_bool_ops_fails_on_scalars():
     _bool_ops_syms = 'and', 'or'
     dtypes = int, float
-    gen = {int: lambda : np.random.randint(10), float: np.random.randn}
+    gen = {int: lambda: np.random.randint(10), float: np.random.randn}
     for engine, parser, dtype1, cmp, dtype2 in product(_engines, expr._parsers,
                                                        dtypes, _bool_ops_syms,
                                                        dtypes):
@@ -1564,6 +1563,19 @@ def test_bool_ops_fails_on_scalars():
                gen[dtype2](), engine, parser)
 
 
+def check_inf(engine, parser):
+    tm.skip_if_no_ne(engine)
+    s = 'inf + 1'
+    expected = np.inf
+    result = pd.eval(s, engine=engine, parser=parser)
+    tm.assert_equal(result, expected)
+
+
+def test_inf():
+    for engine, parser in ENGINES_PARSERS:
+        yield check_inf, engine, parser
+
+
 if __name__ == '__main__':
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
                    exit=False)
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -13284,6 +13284,18 @@ def test_index_resolvers_come_after_columns_with_the_same_name(self):
         expected = df.loc[df.index[df.index > 5]]
         tm.assert_frame_equal(result, expected)
 
+    def test_inf(self):
+        n = 10
+        df = DataFrame({'a': np.random.rand(n), 'b': np.random.rand(n)})
+        df.loc[::2, 0] = np.inf
+        ops = '==', '!='
+        d = dict(zip(ops, (operator.eq, operator.ne)))
+        for op, f in d.items():
+            q = 'a %s inf' % op
+            expected = df[f(df.a, np.inf)]
+            result = df.query(q, engine=self.engine, parser=self.parser)
+            tm.assert_frame_equal(result, expected)
+
 
 class TestDataFrameQueryNumExprPython(TestDataFrameQueryNumExprPandas):