Repository: blaze/blaze

Issue:
.isin() Assertion Error
I'm getting an assertion error with .isin() for blaze when the pandas returns the expected result.

``` python
# recentHist is a blaze object 
historiesDF = bz.into(bz.DataFrame, recentHist)
In [22]: recentUserInteractionsDF = historiesDF[historiesDF.userID.isin(['5539cf3471e30cdb503c0dd8'])]
# Returns expected result

In [23]: recentUserInteractions = recentHist[recentHist.userID=='5539cf3471e30cdb503c0dd8']
in [24]: recentUserInteractions
# Returns expected result

in [25]: recentUserInteractions = recentHist[recentHist.userID.isin(['5539cf3471e30cdb503c0dd8'])]
in [26]: recentUserInteractions
Traceback (most recent call last):

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/IPython/core/formatters.py", line 339, in __call__
    return method()

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/interactive.py", line 329, in <lambda>
    Expr._repr_html_ = lambda x: to_html(x)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 163, in __call__
    return func(*args, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/interactive.py", line 301, in to_html
    return to_html(concrete_head(expr))

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/interactive.py", line 193, in concrete_head
    return odo(head, DataFrame)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/odo/odo.py", line 90, in odo
    return into(target, source, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 163, in __call__
    return func(*args, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/interactive.py", line 316, in into
    result = compute(b, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 163, in __call__
    return func(*args, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/interactive.py", line 178, in compute
    return compute(expr, resources, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 163, in __call__
    return func(*args, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/compute/core.py", line 470, in compute
    result = top_then_bottom_then_top_again_etc(expr3, d4, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/compute/core.py", line 164, in top_then_bottom_then_top_again_etc
    expr2, scope2 = bottom_up_until_type_break(expr, scope, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/compute/core.py", line 348, in bottom_up_until_type_break
    for i in inputs])

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/compute/core.py", line 371, in bottom_up_until_type_break
    **kwargs)}

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 163, in __call__
    return func(*args, **kwargs)

  File "/Users/Dalton/anaconda/envs/dopamine/lib/python2.7/site-packages/blaze/compute/mongo.py", line 215, in compute_up
    assert isinstance(predicate, Broadcast)

AssertionError 

```



Discussion:
Hey @TDaltonC thanks for digging in on these issues! I don't think isin was implemented for mongo. Still, that shouldn't trigger an AssertionError. I'll take a look. 

No problem @cpcloud! Just glad to hear I'm not a nuisance. Here's the ex-issue that made me think about trying it: https://github.com/ContinuumIO/blaze/issues/637

I think that this is a really great project. And I'm glad to play with it.


Resolving patch:
diff --git a/blaze/compute/mongo.py b/blaze/compute/mongo.py
--- a/blaze/compute/mongo.py
+++ b/blaze/compute/mongo.py
@@ -45,13 +45,14 @@
 from __future__ import absolute_import, division, print_function
 
 import numbers
+from operator import attrgetter
 
 from pymongo.collection import Collection
 from pymongo.database import Database
 
 import fnmatch
 from datashape.predicates import isscalar
-from toolz import pluck, first, get
+from toolz import pluck, first, get, compose
 import toolz
 import datetime
 
@@ -61,7 +62,7 @@
                     Eq, Ne, And, Or, Summary, Like, Broadcast, DateTime,
                     Microsecond, Date, Time, Expr, symbol, Arithmetic, floor,
                     ceil, FloorDiv)
-from ..expr.broadcast import broadcast_collect
+from ..expr.broadcast import broadcast_collect, Broadcastable
 from ..expr import math
 from ..expr.datetime import Day, Month, Year, Minute, Second, UTCFromTimestamp
 from ..compatibility import _strtypes
@@ -209,7 +210,14 @@ def compute_up(t, q, **kwargs):
 @dispatch(Selection, MongoQuery)
 def compute_up(expr, data, **kwargs):
     predicate = optimize(expr.predicate, data)
-    assert isinstance(predicate, Broadcast)
+
+    if not isinstance(predicate, Broadcast):
+        raise TypeError("Selection predicate must be a broadcastable "
+                        "operation.\nReceived an expression of type %r.\n"
+                        "Available broadcastable operations are (%s)" %
+                        (type(predicate).__name__,
+                         ', '.join(map(compose(repr, attrgetter('__name__')),
+                                   Broadcastable))))
 
     s = predicate._scalars[0]
     d = dict((s[c], symbol(c, s[c].dshape.measure)) for c in s.fields)


Test patch:
diff --git a/blaze/compute/tests/test_mongo_compute.py b/blaze/compute/tests/test_mongo_compute.py
--- a/blaze/compute/tests/test_mongo_compute.py
+++ b/blaze/compute/tests/test_mongo_compute.py
@@ -43,8 +43,10 @@ def big_bank(db):
             {'name': 'Bob', 'amount': 300, 'city': 'San Francisco'}]
     coll = db.bigbank
     coll = into(coll, data)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 @pytest.yield_fixture
@@ -57,16 +59,20 @@ def date_data(db):
     data = [dict(zip(d.keys(), [d[k][i] for k in d.keys()]))
             for i in range(n)]
     coll = into(db.date_data, data)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 @pytest.yield_fixture
 def bank(db):
     coll = db.bank
     coll = into(coll, bank_raw)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 @pytest.yield_fixture
@@ -77,8 +83,10 @@ def missing_vals(db):
             {'x': 4, 'y': 40}]
     coll = db.missing_vals
     coll = into(coll, data)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 @pytest.yield_fixture
@@ -89,8 +97,10 @@ def points(db):
             {'x': 4, 'y': 40, 'z': 400}]
     coll = db.points
     coll = into(coll, data)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 @pytest.yield_fixture
@@ -100,8 +110,10 @@ def events(db):
             {'time': datetime(2012, 1, 3, 12, 00, 00), 'x': 3}]
     coll = db.events
     coll = into(coll, data)
-    yield coll
-    coll.drop()
+    try:
+        yield coll
+    finally:
+        coll.drop()
 
 
 t = symbol('t', 'var * {name: string, amount: int}')
@@ -374,3 +386,10 @@ def test_interactive_dshape_works():
     d = Data('mongodb://localhost:27017/test_db::bank',
              dshape='var * {name: string, amount: int64}')
     assert d.dshape == dshape('var * {name: string, amount: int64}')
+
+
+@pytest.mark.xfail(raises=TypeError, reason="IsIn not yet implemented")
+def test_isin_fails(bank):
+    expr = t[t.amount.isin([100])]
+    result = compute(expr, bank)
+    assert result == compute(t[t.amount == 100], bank)