Repository: vega/altair

Issue:
Can't use 'count' operation in join aggregate transform
Some join aggregate operations work as expected:


```python
data = pd.DataFrame({
    'x': [0, 1, 2, 0, 1, 2],
    'a': [1, 1, 2, 3, 3, 4],
    'group': [1, 1, 1, 2, 2, 2]
})

import altair as alt

alt.Chart(
    data
).transform_joinaggregate(
    group_sum='sum(a)',
    groupby=['group']
).transform_calculate(
    normalized_a="datum.a / datum.group_sum",
).mark_point().encode(
    x='x:Q',
    y="normalized_a:Q",
    color='group:N',
)
```

![visualization](https://user-images.githubusercontent.com/14128078/67480639-a100fa80-f660-11e9-806f-1ed1d1e13ff1.png)

...but the 'count' operation raises the following error:

```python
import altair as alt

alt.Chart(
    data
).transform_joinaggregate(
    group_count='count()',
    groupby=['group']
).transform_calculate(
    normalized_a="datum.a / datum.group_count",
).mark_point().encode(
    x='x:Q',
    y="normalized_a:Q",
    color='group:N',
)
```

```
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-142-2bf583dd0be9> in <module>()
      5 ).transform_joinaggregate(
      6     group_count='count()',
----> 7     groupby=['group']
      8 ).transform_calculate(
      9     normalized_a="datum.a / datum.group_count",

~/.virtualenvs/simulate/lib/python3.6/site-packages/altair/vegalite/v3/api.py in transform_joinaggregate(self, joinaggregate, groupby, **kwargs)
    929                    'field': parsed.get('field', Undefined),
    930                    'op': parsed.get('aggregate', Undefined)}
--> 931             joinaggregate.append(core.JoinAggregateFieldDef.from_dict(dct))
    932         return self._add_transform(core.JoinAggregateTransform(
    933             joinaggregate=joinaggregate, groupby=groupby

~/.virtualenvs/simulate/lib/python3.6/site-packages/altair/utils/schemapi.py in from_dict(cls, dct, validate, _wrapper_classes)
    366         """
    367         if validate:
--> 368             cls.validate(dct)
    369         if _wrapper_classes is None:
    370             _wrapper_classes = cls._default_wrapper_classes()

~/.virtualenvs/simulate/lib/python3.6/site-packages/altair/utils/schemapi.py in validate(cls, instance, schema)
    402             schema = cls._schema
    403         resolver = jsonschema.RefResolver.from_schema(cls._rootschema or cls._schema)
--> 404         return jsonschema.validate(instance, schema, resolver=resolver)
    405 
    406     @classmethod

~/.virtualenvs/simulate/lib/python3.6/site-packages/jsonschema/validators.py in validate(instance, schema, cls, *args, **kwargs)
    897     error = exceptions.best_match(validator.iter_errors(instance))
    898     if error is not None:
--> 899         raise error
    900 
    901 

ValidationError: Undefined is not of type 'string'

Failed validating 'type' in schema['properties']['field']:
    {'type': 'string'}

On instance['field']:
    Undefined
```

Am I missing something? 

As a side note, if there's a simpler way of normalizing a histogram than using this type of transform, I'd love to hear about it.
Thanks


Discussion:
Thanks for the report - that's a bug, and it should be fixed by #1751. In the meantime, as a workaround you can use ``count(*)``:
```python
chart.transform_joinaggregate(
    group_count='count(*)',
    groupby=['group']
)
```

Resolving patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -915,9 +915,9 @@ def transform_joinaggregate(self, joinaggregate=Undefined, groupby=Undefined, **
         >>> chart.transform[0]
         JoinAggregateTransform({
           joinaggregate: [JoinAggregateFieldDef({
-            as: FieldName('x'),
-            field: FieldName('y'),
-            op: AggregateOp('sum')
+            as: 'x',
+            field: 'y',
+            op: 'sum'
           })]
         })
 
@@ -932,7 +932,7 @@ def transform_joinaggregate(self, joinaggregate=Undefined, groupby=Undefined, **
             dct = {'as': key,
                    'field': parsed.get('field', Undefined),
                    'op': parsed.get('aggregate', Undefined)}
-            joinaggregate.append(core.JoinAggregateFieldDef.from_dict(dct))
+            joinaggregate.append(core.JoinAggregateFieldDef(**dct))
         return self._add_transform(core.JoinAggregateTransform(
             joinaggregate=joinaggregate, groupby=groupby
         ))


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -402,9 +402,9 @@ def test_transforms():
     kwds = {
         'joinaggregate': [
             alt.JoinAggregateFieldDef(
-                field=alt.FieldName('x'),
-                op=alt.AggregateOp('min'),
-                **{'as': alt.FieldName('min')})
+                field='x',
+                op='min',
+                **{'as': 'min'})
         ],
         'groupby': ['key']
     }