Repository: altair-viz/altair
Pull request #578 (2018-03-14 16:51:20)

ENH: parse shorthand within alt.facet()
This also makes ``facet()`` a method of ``Chart`` and ``LayerChart`` rather than a top-level function. While writing the docs for facet, it occurred to me that this is a more consistent API (it's similar to ``repeat``)


Patch:
diff --git a/altair/vegalite/v2/api.py b/altair/vegalite/v2/api.py
index ad414945..e8f74114 100644
--- a/altair/vegalite/v2/api.py
+++ b/altair/vegalite/v2/api.py
@@ -580,6 +580,20 @@ class Chart(TopLevelMixin, mixins.MarkMethodMixin, core.TopLevelFacetedUnitSpec)
                                     encodings=encodings)
         return copy
 
+    def facet(self, row=Undefined, column=Undefined, data=Undefined, **kwargs):
+        """Create a facet chart from the current chart.
+
+        Faceted charts require data to be specified at the top level; if data
+        is not specified, the data from the current chart will be used at the
+        top level.
+        """
+        if data is Undefined:
+            data = self.data
+            self = self.copy()
+            self.data = Undefined
+        return FacetChart(self, row=row, column=column, data=data, **kwargs)
+
+
 
 def _check_if_valid_subspec(spec, classname):
     """Check if the spec is a valid sub-spec.
@@ -750,6 +764,19 @@ class LayerChart(TopLevelMixin, core.TopLevelLayerSpec):
         copy.layer[0] = copy.layer[0].interactive(name=name, bind_x=bind_x, bind_y=bind_y)
         return copy
 
+    def facet(self, row=Undefined, column=Undefined, data=Undefined, **kwargs):
+        """Create a facet chart from the current chart.
+
+        Faceted charts require data to be specified at the top level; if data
+        is not specified, the data from the current chart will be used at the
+        top level.
+        """
+        if data is Undefined:
+            data = self.data
+            self = self.copy()
+            self.data = Undefined
+        return FacetChart(self, row=row, column=column, data=data, **kwargs)
+
 
 def layer(*charts, **kwargs):
     """layer multiple charts"""
@@ -765,16 +792,15 @@ class FacetChart(TopLevelMixin, core.TopLevelFacetSpec):
         if facet is Undefined:
             facet = core.FacetMapping()
         if row is not Undefined:
+            if isinstance(row, six.string_types):
+                row = core.FacetFieldDef(**utils.parse_shorthand(row))
             facet['row'] = row
         if column is not Undefined:
+            if isinstance(column, six.string_types):
+                column = core.FacetFieldDef(**utils.parse_shorthand(column))
             facet['column'] = column
         if 'data' not in kwargs:
             warnings.warn('FacetChart: data should be defined at the top level')
         super(FacetChart, self).__init__(spec=spec, facet=facet, **kwargs)
 
     # TODO: think about the most useful class API here
-
-
-def facet(spec, row=Undefined, column=Undefined, **kwargs):
-    """Create a FacetChart"""
-    return FacetChart(spec=spec, row=row, column=column, **kwargs)


Test patch:
diff --git a/altair/vegalite/v2/tests/test_api.py b/altair/vegalite/v2/tests/test_api.py
index a00da648..a887dbaa 100644
--- a/altair/vegalite/v2/tests/test_api.py
+++ b/altair/vegalite/v2/tests/test_api.py
@@ -159,3 +159,18 @@ def test_savechart(format):
     elif format == 'html':
         content = out.read()
         assert content.startswith('\n<!DOCTYPE html>')
+
+
+def test_facet_parse():
+    chart = alt.Chart('data.csv').mark_point().encode(
+        x='x:Q',
+        y='y:Q'
+    ).facet(
+        row='row:N',
+        column='column:O'
+    )
+    dct = chart.to_dict()
+    assert dct['data'] == {'url': 'data.csv'}
+    assert 'data' not in dct['spec']
+    assert dct['facet'] == {'column': {'field': 'column', 'type': 'ordinal'},
+                            'row': {'field': 'row', 'type': 'nominal'}}