Repository: vega/altair

Issue:
Using field predicates in conditions
Hi -- first off, thanks for developing Altair! It's already been very useful for our work.

I've been looking into implementing conditional `size` encodings for marks in a visualization based on the value of an attribute. I figured I might do this by using a `FieldEqualPredicate` inside a `condition`, but every time I tried to use this predicate by itself in a `condition` I got the following error:

```python
NotImplementedError: condition predicate of type <class 'altair.vegalite.v2.schema.core.FieldEqualPredicate'>
```

As an example of this, here's the Jupyter Notebook example code from the [installation page](https://altair-viz.github.io/getting_started/installation.html?highlight=data%20iris#quick-start-altair-notebook), with the only addition being a `size` argument that results in this error. The intended behavior of this `size` argument would be making each point with a `petalWidth` of 1 larger than the other points in the plot.

```python
import altair as alt
from vega_datasets import data

alt.renderers.enable('notebook')

iris = data.iris()

alt.Chart(iris).mark_point().encode(
    x='petalLength',
    y='petalWidth',
    color='species',
    size=alt.condition(
        alt.FieldEqualPredicate(field='petalWidth', equal=1.0),
        alt.value(30.0),
        alt.value(5.0)
    )
)
```

Wrapping the `FieldEqualPredicate` inside a `Predicate` makes this work, though:

```python
...
    size=alt.condition(
        alt.Predicate(
            alt.FieldEqualPredicate(field='petalWidth', equal=1.0)
        ),
        alt.value(30.0),
        alt.value(5.0)
    )
...
```

Is this current behavior (of field predicates being required to be wrapped inside a general `Predicate` when being used in a `condition`) intentional? I can't find any explicit documentation of this requirement outside of the description of the `predicate` parameter of [`altair.condition()`](https://altair-viz.github.io/user_guide/generated/api/altair.condition.html) (and without some knowledge of the various `Predicate` object hierarchies in Altair, it seems at first like a field predicate would be usable by itself for that parameter).

If this behavior is intentional, I think it might be a good idea to update the docs to make this more clear. (As mentioned, from just looking at the `condition` ([1](https://altair-viz.github.io/user_guide/generated/api/altair.condition.html)) and `FieldEqualPredicate` ([1](https://altair-viz.github.io/user_guide/generated/core/altair.FieldEqualPredicate.html), [2](https://altair-viz.github.io/user_guide/transform.html#field-predicates)) docs, it isn't immediately clear why the first code example in this issue fails.) In either case, I'd be happy to look into making a pull request to either change this (so that field predicates can be directly passed into conditions) or update the documentation about this behavior.

Thanks again for Altair! :)


Discussion:
This is a bug –  we need to update the list of recognized predicates here: https://github.com/altair-viz/altair/blob/master/altair/vegalite/v2/api.py#L334-L336
Long-term, the best fix for this would be to have schemapi auto-generate the class hierarchy implied within the vega-lite schema (so all of these predicate classes would be derived from the ``Predicate`` base class) but I think that would take a fair bit of work.

Resolving patch:
diff --git a/altair/vegalite/v2/api.py b/altair/vegalite/v2/api.py
--- a/altair/vegalite/v2/api.py
+++ b/altair/vegalite/v2/api.py
@@ -333,7 +333,11 @@ def condition(predicate, if_true, if_false, **kwargs):
                             core.SelectionAnd, core.SelectionOperand)
     test_predicates = (six.string_types, expr.Expression, core.Predicate,
                        core.LogicalOperandPredicate, core.LogicalNotPredicate,
-                       core.LogicalOrPredicate, core.LogicalAndPredicate)
+                       core.LogicalOrPredicate, core.LogicalAndPredicate,
+                       core.FieldEqualPredicate, core.FieldOneOfPredicate,
+                       core.FieldRangePredicate, core.FieldLTPredicate,
+                       core.FieldGTPredicate, core.FieldLTEPredicate,
+                       core.FieldGTEPredicate, core.SelectionPredicate)
 
     if isinstance(predicate, NamedSelection):
         condition = {'selection': predicate._get_name()}


Test patch: