<commit_before><commit_msg>Added initial version of Columnar_Data Tutorial<commit_after>{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this Tutorial we will explore how to work with Columnar data in HoloViews. The majority of data we work with on a daily basis can be described in columns from spreadsheets to SQL data. HoloViews defines an extensible system of interfaces to load, operate on and visualize this kind of data.\n",
    "\n",
    "By default HoloViews will use one of three interfaces to operate on this data:\n",
    "\n",
    "* A pure Python dictionary containing each column.\n",
    "* A purely numpy based format for numeric data.\n",
    "* Pandas DataFrames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import holoviews as hv\n",
    "hv.notebook_extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Simple Columns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Usually when working with data we have one or more independent variables, taking the form of categories, discrete samples or bins, these are what we refer to as key dimensions or kdims for short in HoloViews. The observed or dependent variables on the other hand are referred to as value dimensions (vdims). The simplest form of a Columns object is therefore a column 'x' and a column 'y' corresponding to the key dimensions and value dimensions respectively. A simple visual representation of this data is a Table, however there are many differ ways to represent this data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "xs = range(10)\n",
    "ys = np.linspace(0, 1, 10)\n",
    "table = hv.Table((xs, ys), kdims=['x'], vdims=['y'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However this data has many more meaningful visual representations, therefore the first important concept is that Columns objects are interchangeable as long as their dimensionality allows it, meaning that you can easily cast between them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "hv.Scatter(table) + hv.Curve(table) + hv.Bars(table)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The choice of an appropriate Element depends on the dimensionality of the data, here's a rough listing of the dimensionality of different Element types:\n",
    "\n",
    "* 0D: BoxWhisker, Spikes, Distribution*, \n",
    "* 1D: Scatter, Curve, ErrorBars, Spread, Bars, BoxWhisker, Regression*\n",
    "* 2D: Points, HeatMap, Bars, BoxWhisker, Bivariate*\n",
    "* 3D: Scatter3D, Trisurface, VectorField, BoxWhisker, Bars\n",
    "\n",
    "\\* - requires Seaborn\n",
    "\n",
    "This only represents the dimensionality of the actual sampling of each Element. Additionally an Element can have any number of value dimensions, which may be mapped onto various attributes of a plot such as the color, size, angle of the plot elements, for a reference of how to use these various Element types see the [Elements Tutorial](Elements)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data types and Constructors\n",
    "\n",
    "As discussed above Columns provide an extensible interface to store and operate on data in different formats. All interfaces support a number of standard constructors:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Literals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "print repr(hv.Scatter(ys) + hv.Scatter((xs, ys)) + hv.Scatter(zip(xs, ys)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Storage formats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "print repr(hv.Scatter({'x': xs, 'y': ys}) +\n",
    "           hv.Scatter(np.column_stack([xs, ys])) +\n",
    "           hv.Scatter(pd.DataFrame({'x': xs, 'y': ys})))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To select a particular storage format supply a one or more allowed datatypes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "print type(hv.Scatter((xs, ys), datatype=['array']).data)\n",
    "print type(hv.Scatter((xs, ys), datatype=['dictionary']).data)\n",
    "print type(hv.Scatter((xs, ys), datatype=['dataframe']).data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Columns will attempt to parse the supplied data falling back to each consecutive interface if the previous could not interpret the data. The default list of fallbacks and simultaneously the list of allowed datatypes is:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "hv.Columns.datatype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default Columns will try to construct a simple array, falling back to either pandas dataframes (if available) or the dictionary based format if the data is not purely numeric. Additionally the interfaces will try to maintain their type, numpy arrays and pandas DataFrame will therefore always be parsed by the array and dataframe interfaces first respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "df = pd.DataFrame({'x': xs, 'y': ys, 'z': ys*2})\n",
    "print type(hv.Scatter(df).data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Sharing Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since the formats with labelled columns do not require any specific order the Elements can effectively become views into the data. By specifying different key and value dimensions many Elements can share the same data source."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "overlay = hv.Scatter(df, kdims='x', vdims='y') * hv.Scatter(df, kdims='x', vdims='z')\n",
    "overlay"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can quickly confirm that the data is actually shared:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "overlay.Scatter.I.data is overlay.Scatter.II.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is much more efficient than creating copies of the data for each Element and allows for some advanced features like linked brushing in the [Bokeh backend](Bokeh_Backend)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Converting to raw data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Column types make it easy to export the data to the three basic formats, arrays, dataframes and a dictionary of columns.\n",
    "\n",
    "###### Array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "table.array()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Pandas DataFrame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "table.dframe().head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Columns dictionary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "table.columns()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Applying operations to the data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Basic Operations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "bars = hv.Bars((['C', 'A', 'B', 'D'], [2, 7, 3, 4]))\n",
    "bars + bars.sort(['x'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Working with categorical or grouped data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Data is often grouped in various ways and the Columns interface provides various means to easily compare between groups and apply statistical aggregates. We'll start by generating some synthetic data with two groups along the x-axis and 4 groups along the y axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "n = np.arange(1000)\n",
    "xs = np.repeat(range(2), 500)\n",
    "ys = n%4\n",
    "zs = np.random.randn(1000)\n",
    "table = hv.Table((xs, ys, zs), kdims=['x', 'y'], vdims=['z'])\n",
    "table"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since there are repeat observations of the same x- and y-values we have to reduce the data before we display it or use a datatype that supports plotting distributions in this way. The ``BoxWhisker`` type allows doing exactly that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts BoxWhisker [aspect=2 fig_size=200 bgcolor='w']\n",
    "hv.BoxWhisker(table)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Aggregating/Reducing dimensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Most types require the data to be non-duplicated before being displayed for this purpose HoloViews makes it easy to ``aggregate`` and ``reduce`` the data. These two operations are simple inverses of each other, aggregate computes a statistic for each group in the supplied dimensions, while reduce aggregates over all the groups except the supplied dimensions. Supplying only a function and no dimensions will simply aggregate or reduce all available key dimensions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Bars [show_legend=False] {+axiswise}\n",
    "hv.Bars(table).aggregate(function=np.mean) + hv.Bars(table).reduce(x=np.mean)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(**A**) aggregates over both the x and y dimension, computing the mean for each x/y group, while (**B**) reduces the x dimension leaving just the mean for each group along y."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Collapsing multi Columns Elements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When multiple observations are broken out into a HoloMap they can easily be combined using the ``collapse`` method. Here we create a number of Curves with increasingly larger y-values. By collapsing them with a ``function`` and a ``spreadfn`` we can compute the mean curve with a confidence interval. We simply cast the collapsed ``Curve`` to a ``Spread`` and ``Curve`` Element to visualize them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "hmap = hv.HoloMap({i: hv.Curve(np.arange(10)*i) for i in range(10)})\n",
    "collapsed = hmap.collapse(function=np.mean, spreadfn=np.std)\n",
    "hv.Spread(collapsed) * hv.Curve(collapsed) + collapsed.table()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Working with complex data\n",
    "\n",
    "In the last section we only scratched the surface of what the Columns interface can do, when it really comes into its own is when working with high dimensional datasets. We'll load a dataset of some macro-economic indicators for a OECD countries from 1964-1990 from the HoloViews website."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "macro_df = pd.read_csv('http://ioam.github.com/holoviews/Tutorials/macro.csv', '\\t')\n",
    "\n",
    "dimensions = {'unem': 'Unemployment',\n",
    "              'capmob': 'Capital Mobility',\n",
    "              'gdp': 'GDP Growth', 'trade': 'Trade',\n",
    "              'year': 'Year', 'country': 'Country'}\n",
    "\n",
    "macro_df = macro_df.rename(columns=dimensions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll also take this opportunity to set options for all the following plots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%output dpi=100\n",
    "options = hv.Store.options()\n",
    "opts = hv.Options('plot', aspect=2, fig_size=250, show_frame=False, show_grid=True, legend_position='right')\n",
    "options.NdOverlay = opts\n",
    "options.Overlay = opts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Loading the data\n",
    "\n",
    "As we saw above we can supply the dataframe to any Columns type. When dealing with so many dimensions it would be cumbersome to supply all the dimensions explicitly, therefore Columns can easily infer the dimensions from a dataframe. We simply supply the ``kdims`` and it will infer that all other numeric dimensions should be treated as value dimensions (``vdims``)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "macro = hv.Table(macro_df, kdims=['Year', 'Country'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get an overview of the data we'll quickly sort it and then view the data for one year."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Table [aspect=1.5 fig_size=300]\n",
    "macro = macro.sort()\n",
    "macro[1988]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above we looked at converting a Table to simple Element types, however HoloViews also provides powerful container objects to explore high-dimensional data, currently these are [HoloMap](http://ioam.github.io/holoviews/Tutorials/Containers.html#HoloMap), [NdOverlay](http://ioam.github.io/holoviews/Tutorials/Containers.html#NdOverlay), [NdLayout](http://ioam.github.io/holoviews/Tutorials/Containers.html#NdLayout) and [GridSpace](http://ioam.github.io/holoviews/Tutorials/Containers.html#Layout). HoloMaps provide the basic conversion type from which you can conveniently convert to the other container types using the ``.overlay``, ``.layout`` and ``.grid`` methods. This way we can easily create an overlay of GDP Growth curves by year for each country. Here 'Year' is a key dimension and 'GDP Growth' a value dimension. Additionally we are left with the 'Country' dimension, which we then overlay calling the ``.overlay method``."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Curve (color=Palette('Set3'))\n",
    "gdp_curves = macro.to.curve('Year', 'GDP Growth')\n",
    "gdp_curves.overlay('Country')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we've extracted the gdp_curves we can apply some operations to them. As in the simpler example above we will ``collapse`` the HoloMap of Curves using a number of functions to visualize the distribution of GDP Growth rates over time. First we find the mean curve with np.std as the ``spreadfn`` and cast the result to a ``Spread`` type, then we compute the min, mean and max curve in the same way and put them inside an Overlay."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Overlay [bgcolor='w' legend_position='top_right'] Curve (color='k' linewidth=1) Spread (color='gray' alpha=0.2)\n",
    "hv.Spread(gdp_curves.collapse('Country', np.mean, np.std), label='std') *\\\n",
    "hv.Overlay([gdp_curves.collapse('Country', fn).relabel(name)(style=dict(linestyle=ls))\n",
    "            for name, fn, ls in [('max', np.max, '--'), ('mean', np.mean, '-'), ('min', np.min, '--')]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many HoloViews Element types support multiple kdims, including HeatMaps, Points, Scatter, Scatter3D, and Bars. Bars in particular allows you to lay out your data in groups, categories and stacks. By supplying the index of that dimension as a plotting option you can choose to lay out your data as groups of bars, categories in each group and stacks. Here we choose to lay out the trade surplus of each country with groups for each year, no categories, and stacked by country. Finally we choose to color the Bars for each item in the stack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%opts Bars [bgcolor='w' aspect=3 figure_size=450 show_frame=False]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Bars [category_index=2 stack_index=0 group_index=1 legend_position='top' legend_cols=7 color_by=['stack']] (color=Palette('Dark2'))\n",
    "macro.to.bars(['Country', 'Year'], 'Trade', [])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the .select method we can pull out the data for just a few countries and specific years. We can also make more advanced use the Palettes.\n",
    "\n",
    "Palettes can customized by selecting only a subrange of the underlying cmap to draw the colors from. The Palette draws samples from the colormap using the supplied sample_fn, which by default just draws linear samples but may be overriden with any function that draws samples in the supplied ranges. By slicing the Set1 colormap we draw colors only from the upper half of the palette and then reverse it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Bars [padding=0.02 color_by=['group']] (alpha=0.6, color=Palette('Set1', reverse=True)[0.:.2])\n",
    "countries = {'Belgium', 'Netherlands', 'Sweden', 'Norway'}\n",
    "macro.to.bars(['Country', 'Year'], 'Unemployment').select(Year=(1978, 1985), Country=countries)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many HoloViews Elements support multiple key and value dimensions. A HeatMap may be indexed by two kdims, so we can visualize each of the economic indicators by year and country in a Layout. Layouts are useful for heterogeneous data you want to lay out next to each other.\n",
    "\n",
    "Before we display the Layout let's apply some styling, we'll suppress the value labels applied to a HeatMap by default and substitute it for a colorbar. Additionally we up the number of xticks that are drawn and rotate them by 90 degrees to avoid overlapping. Flipping the y-axis ensures that the countries appear in alphabetical order. Finally we reduce some of the margins of the Layout and increase the size."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%opts HeatMap [show_values=False xticks=40 xrotation=90 invert_yaxis=True]\n",
    "%opts Layout [figure_size=150] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "hv.Layout([macro.to.heatmap(['Year', 'Country'], value)\n",
    "           for value in macro.data.columns[2:]]).cols(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another way of combining heterogeneous data dimensions is to map them to a multi-dimensional plot type. Scatter Elements for example support multiple ``vdims``, which may be mapped onto the color and size of the drawn points in addition to the y-axis position. \n",
    "\n",
    "As for the Curves above we supply 'Year' as the sole key dimension and rely on the Table to automatically convert the Country to a map dimension, which we'll overlay. However this time we select both GDP Growth and Unemployment but to be plotted as points. To get a sensible chart, we adjust the scaling_factor for the points to get a reasonable distribution in sizes and apply a categorical Palette so we can distinguish each country."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Scatter [scaling_factor=1.4] (color=Palette('Set3') edgecolors='k')\n",
    "gdp_unem_scatter = macro.to.scatter('Year', ['GDP Growth', 'Unemployment'])\n",
    "gdp_unem_scatter.overlay('Country')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this way we can plot any dimension against any other dimension very easily allowing us to iterate through different ways of revealing relationships in the dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts NdOverlay [legend_cols=2] Scatter [size_index=1 scaling_factor=1.3] (color=Palette('Blues'))\n",
    "macro.to.scatter('GDP Growth', 'Unemployment', ['Year']).overlay()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This view for example immediately highlights the high unemployment rates of the 80s."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since all HoloViews Elements are composable we can generate complex figures just by applying the * operator. We'll simply reuse the GDP curves we generated earlier, combine them with the scatter points, which indicate the unemployment rate by size and annotate the data with some descriptions of what happened economically in these years."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Curve (color='k') Scatter [color_index=2 size_index=2 scaling_factor=1.4] (cmap='Blues' edgecolors='k')\n",
    "macro_overlay = gdp_curves * gdp_unem_scatter\n",
    "annotations = hv.Arrow(1973, 8, 'Oil Crisis', 'v') * hv.Arrow(1975, 6, 'Stagflation', 'v') *\\\n",
    "hv.Arrow(1979, 8, 'Energy Crisis', 'v') * hv.Arrow(1981.9, 5, 'Early Eighties\\n Recession', 'v')\n",
    "macro_overlay * annotations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since we didn't map the country to some other container type, we get a widget allowing us to view the plot separately for each country, reducing the forest of curves we encountered before to manageable chunks. \n",
    "\n",
    "While looking at the plots individually like this allows us to study trends for each country, we may want to lay outa subset of the countries side by side. We can easily achieve this by selecting the countries we want to view and and then applying the ``.layout`` method. We'll also want to restore the aspect so the plots compose nicely."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%opts Overlay [aspect=1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts NdLayout [figure_size=100] Scatter [color_index=2] (cmap='Reds')\n",
    "countries = {'United States', 'Canada', 'United Kingdom'}\n",
    "(gdp_curves * gdp_unem_scatter).select(Country=countries).layout('Country')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally let's combine some plots for each country into a Layout, giving us a quick overview of each economic indicator for each country:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%opts Layout [fig_size=100] Scatter [color_index=2] (cmap='Reds')\n",
    "(macro_overlay.relabel('GDP Growth', depth=1) +\\\n",
    "macro.to.curve('Year', 'Unemployment', ['Country'], group='Unemployment',) +\\\n",
    "macro.to.curve('Year', 'Trade', ['Country'], group='Trade') +\\\n",
    "macro.to.scatter('GDP Growth', 'Unemployment', ['Country'])).cols(2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
{'fraction_non_alphanumeric': 0.156329522579665, 'fraction_numerical': 0.006038968437844202, 'mean_word_length': 3.27365687388411, 'pattern_counts': {'":': 360, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6794262', 'n_tokens_mistral': 8783, 'n_tokens_neox': 8412, 'n_words': 2709}