Repository: vega/altair

Issue:
Improve facet wrap
The ``facet()`` method of the chart does not easily allow specifying wrapped facets. This should be improved.

I think, for example
```
chart.facet(row='x', col='y')
```
should do old-style faceting, while
```
chart.facet('x', columns=2)
```
should do new-style wrapped faceting.


Discussion:

Resolving patch:
diff --git a/altair/examples/us_population_over_time_facet.py b/altair/examples/us_population_over_time_facet.py
new file mode 100644
--- /dev/null
+++ b/altair/examples/us_population_over_time_facet.py
@@ -0,0 +1,26 @@
+"""
+US Population: Wrapped Facet
+============================
+This chart visualizes the age distribution of the US population over time,
+using a wrapped faceting of the data by decade.
+"""
+# category: case studies
+import altair as alt
+from vega_datasets import data
+
+source = data.population.url
+
+alt.Chart(source).mark_area().encode(
+    x='age:O',
+    y=alt.Y(
+        'sum(people):Q',
+        title='Population',
+        axis=alt.Axis(format='~s')
+    ),
+    facet='year:O'
+).properties(
+    title='US Age Distribution By Year',
+    columns=5,
+    width=90,
+    height=80
+)
\ No newline at end of file
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
@@ -1434,19 +1434,56 @@ def _wrap_in_channel_class(obj, prop):
         copy.encoding = core.FacetedEncoding(**encoding)
         return copy
 
-    def facet(self, row=Undefined, column=Undefined, data=Undefined, **kwargs):
+    def facet(self, facet=Undefined, row=Undefined, column=Undefined, data=Undefined,
+              columns=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.
+
+        Parameters
+        ----------
+        facet : string or alt.Facet (optional)
+            The data column to use as an encoding for a wrapped facet.
+            If specified, then neither row nor column may be specified.
+        column : string or alt.Column (optional)
+            The data column to use as an encoding for a column facet.
+            May be combined with row argument, but not with facet argument.
+        row : string or alt.Column (optional)
+            The data column to use as an encoding for a row facet.
+            May be combined with column argument, but not with facet argument.
+        data : string or dataframe (optional)
+            The dataset to use for faceting. If not supplied, then data must
+            be specified in the top-level chart that calls this method.
+        columns : integer
+            the maximum number of columns for a wrapped facet.
+
+        Returns
+        -------
+        self :
+            for chaining
         """
+        facet_specified = (facet is not Undefined)
+        rowcol_specified = (row is not Undefined or column is not Undefined)
+
+        if facet_specified and rowcol_specified:
+            raise ValueError("facet argument cannot be combined with row/column argument.")
+
         if data is Undefined:
-            data = self.data
-            self = self.copy()
-            self.data = Undefined
-        return FacetChart(spec=self, facet=FacetMapping(row=row, column=column),
-                          data=data, **kwargs)
+            if self.data is Undefined:
+                 raise ValueError("Facet charts require data to be specified at the top level.")
+            self = self.copy(ignore=['data'])
+            data, self.data = self.data, Undefined
+
+        if facet_specified:
+            if isinstance(column, six.string_types):
+                column = channels.Facet(column)
+            facet = column
+        else:
+            facet = FacetMapping(row=row, column=column)
+
+        return FacetChart(spec=self, facet=facet, data=data, columns=columns, **kwargs)
 
 
 class Chart(TopLevelMixin, EncodingMixin, mixins.MarkMethodMixin,


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
@@ -491,6 +491,7 @@ def test_chart_from_dict():
               base | base,
               base & base,
               base.facet(row='c:N'),
+              (base + base).facet(row='c:N', data='data.csv'),
               base.repeat(row=['c:N', 'd:N'])]
 
     for chart in charts: