{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Customizing Plots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import holoviews as hv\n",
    "hv.extension('bokeh', 'matplotlib')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "As introduced in the [Customization](../getting_started/2-Customization.ipynb) section of the 'Getting Started' guide, HoloViews separates the notion of content from presentation. This is achieved using a tree datastructure containing simply keyword values that specify how elements are to appear, either by type, by their ``group`` and ``label`` specification as introduced in [Annotating Data](../user_guide/01-Annotating_Data.ipynb) or for individual element instances.\n",
    "\n",
    "In addition, HoloViews has the notion of ``Renderer`` instance that normally work behind the scenes to control how output is generated for the selected plotting extension. In this guide, we will first show how you can customize the visual styling with the options system and then how you can easily control the output options of renderers."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Visual options and styling\n",
    "\n",
    "The different types of visual options will be illustrated using the example introduced in the [Customization](../getting_started/2-Customization.ipynb) getting started guide. First we create a ``curve`` and a ``spikes`` element:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "spike_train = pd.read_csv('../assets/spike_train.csv.gz')\n",
    "curve  = hv.Curve( spike_train, 'milliseconds', 'Hertz')\n",
    "spikes = hv.Spikes(spike_train, 'milliseconds', [])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we display it as we did in the getting started guide:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output size=150\n",
    "%%opts Curve  [height=100 width=600 xaxis=None tools=['hover']]\n",
    "%%opts Curve (color='red' line_width=1.5)\n",
    "%%opts Spikes [height=100 width=600 yaxis=None] (color='grey' line_width=0.25)\n",
    "curve  = hv.Curve( spike_train, 'milliseconds', vdims='Hertz')\n",
    "spikes = hv.Spikes(spike_train, 'milliseconds', vdims=[])\n",
    "(curve+spikes).cols(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Introducing ``plot`` and ``style`` options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "This example allows us to expand on the concepts introduced in the [Customization](../getting_started/2-Customization.ipynb) getting started guide. First, we note that two things are immediately apparent (1) almost all the customization is going on at the top of the cell, leaving the declaration of our elements and layout nearly unchanged and (2) this customization syntax that is specific to IPython and the Jupyter notebook and is *not* valid Python. We will use this syntax in this section but it is important to note that we have regular Python equivalents to these tools as will be described shortly.\n",
    "\n",
    "Let's start with the one difference to how our layout is declared, namely the use of ``.cols(1)`` at the end. If you delete the lines starting with ``%`` at the top of the cell and re-execute it, you will see this simply rearranges the default layout to a single column format, with the curve *above* the spikes instead of next to it.\n",
    "\n",
    "Now let us go through the lines starting with ``%%output`` and ``%%opts``, starting with the first line:\n",
    "\n",
    "```python\n",
    "%%output size=150\n",
    "```\n",
    "\n",
    "This is the *output cell magic* which customizes the output format of a particular cell by instructing the active plotting renderer with the desired size. There is a corresponding *cell line magic* to set things globally and a ``hv.output`` utility (for both local and global settings) that you can read about below. All these output tools accept a ``size`` options which in this case sets our plot size to 150% of the default.\n",
    "\n",
    "Next we have:\n",
    "\n",
    "```python\n",
    "%%opts Curve  [height=100 width=600 xaxis=None show_grid=False tools=['hover']]\n",
    "```\n",
    "\n",
    "This is the *opts cell magic* which customizes the visual presentation of elements displayed in a particular cell. In this case, it specifies a set of keywords relevant to our ``Curve`` element in square brackets. These list of keywords are ***plot options*** that specify the settings that HoloViews is to use to plot ``Curve`` elements.\n",
    "\n",
    "In this case we want to make an elongated ``Curve`` plot to make it easier to view the timeseries and to allow for a corresponding elongated spikes plot underneath which is why we specified the ``width`` and ``height`` keywords. We also want to disable the ``xaxis`` (our ``Curve`` plot will give us a suitable millisecond below). Finally with ``tools=['hover']`` we add the [Bokeh hover tool](http://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#hovertool) to our plot: try hovering your pointer over the red curve to see the exact values of each sample!\n",
    "\n",
    "The next line specifies a second set of keywords relating to our ``Curve`` element:\n",
    "\n",
    "```python\n",
    "%%opts Curve (color='red' line_width=1.5)\n",
    "```\n",
    "\n",
    "In this case, our list of keywords are in *parentheses* specifying  ***style options*** with are Bokeh specific and passed down directly from HoloViews to Bokeh. The ``color`` and ``line_width`` options are *not* part of the HoloViews API but keywords used to customize the appearance of [Bokeh glyphs](http://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html).\n",
    "\n",
    "\n",
    "Lastly we have use the ``%%opts`` cell magic to customize the appearance of our ``Spikes`` element:\n",
    "\n",
    "```python\n",
    "%%opts Spikes [height=100 width=600 yaxis=None] (color='grey' line_width=0.25)\n",
    "```\n",
    "\n",
    "This introduces no new concepts other than the fact that you can declare plot and style options on the same line. In these plot options, we match the elongated height and width values of the curve and disable the yaxis. In the style options we make the spikes 'grey' in color with a ``line_width`` value of 0.25.\n",
    "\n",
    "In addition to ``plot`` and ``style`` options, there are normalization ``norm`` options that will be discussed shortly. Available options for individual elements can be listed using:\n",
    "\n",
    "```python\n",
    "# note, a backend needs to be initialized and active for hv.help to return available options\n",
    "hv.help(obj)\n",
    "```\n",
    "\n",
    "Now that we have worked through concrete examples, we can now describe the syntax used in more detail."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Option specification syntax\n",
    "\n",
    "Here are the three types of visualization options in HoloViews:\n",
    "\n",
    "#### Option types\n",
    "\n",
    "##### ``style`` options:\n",
    "\n",
    "``style`` options are passed directly to the underlying rendering backend that actually draws the plots, allowing you to control the details of how it behaves.  The default backend is matplotlib, but there are other backends either using matplotlib's options (e.g. ``mpld3``), or their own sets of options (e.g. [``bokeh``](Bokeh_Backend) ).\n",
    "\n",
    "For whichever backend has been selected, HoloViews can tell you which options are supported, but you will need to read the corresponding documentation (e.g. [matplotlib](http://matplotlib.org/contents.html), [bokeh](http://bokeh.pydata.org)) for the details of their use. For listing available options, see the ``hv.help``-mechanism mentioned in the previous section.\n",
    "\n",
    "HoloViews has been designed to be easily extensible to additional backends in the future, such as [Plotly](https://github.com/ioam/holoviews/pull/398), Cairo, VTK, or D3.js, and if one of those backends were selected then the supported style options would differ.\n",
    "\n",
    "##### ``plot`` options:\n",
    "\n",
    "Each of the various HoloViews plotting classes declares various [Parameters](http://ioam.github.io/param) that control how HoloViews builds the visualization for that type of object, such as plot sizes and labels.  HoloViews uses these options internally; they are not simply passed to the underlying backend.  HoloViews documents these options fully in its online help and in the [Reference Manual](http://holoviews.org/Reference_Manual).  These options may vary for different backends in some cases, depending on the support available both in that library and in the HoloViews interface to it, but we try to keep any options that are meaningful for a variety of backends the same for all of them. For listing available options, see the ``hv.help``-mechanism mentioned in the previous section.\n",
    "\n",
    "##### ``norm`` options:\n",
    "\n",
    "``norm`` options are a special type of plot option that are applied orthogonally to the above two types, to control normalization.  Normalization refers to adjusting the properties of one plot relative to those of another.  For instance, two images normalized together would appear with relative brightness levels, with the brightest image using the full range black to white, while the other image is scaled proportionally.  Two images normalized independently would both cover the full range from black to white.  Similarly, two axis ranges normalized together will expand to fit the largest range of either axis, while those normalized separately would cover different ranges. See the ``norm`` options section below. For listing available options, see the ``hv.help``-mechanism mentioned in the previous section.\n",
    "\n",
    "#### Selecting with ``type``, ``group`` and ``label``\n",
    "\n",
    "The three types of option mentioned above can be associated with HoloViews objects at three levels of specificity: at the level of the element *type*, by the assigned element *group* and then by the assigned *label*.\n",
    "\n",
    "Here is an example of the three levels of specificity using different curves below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Curve (color='blue')\n",
    "%%opts Curve.Sinusoid (color='red')\n",
    "%%opts Curve.Sinusoid.Squared [interpolation='steps-mid'] (color='green')\n",
    "xs = np.linspace(-np.pi,np.pi,100)\n",
    "curve = hv.Curve((xs, xs/3))\n",
    "group_curve1 = hv.Curve((xs, np.sin(xs)), group='Sinusoid')\n",
    "group_curve2 = hv.Curve((xs, np.sin(xs+np.pi/4)), group='Sinusoid')\n",
    "label_curve = hv.Curve((xs, np.sin(xs)**2), group='Sinusoid', label='Squared')\n",
    "layout = curve * group_curve1 * group_curve2 * label_curve\n",
    "layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The straight line has no ``group`` and ``label`` so it gets 'blue' from the ``Curve`` level of specificity. The two sine curves are red as they both have the ``group`` specification of 'Sinusoid'. Lastly we has a sine squared curve with the same ``group`` label of 'Sinusoid' but it also has the ``label`` 'Squared' which is why it is green.\n",
    "\n",
    "#### Dictionary format\n",
    "\n",
    "HoloViews avoids string parsing and special syntax (other than the basic operators described in [Composing Elements](./02-Composing_Elements.ipynb)) where possible. For this reason, all options are fundamentally reduced to a simple dictionary format. For example, here is the pure Python equivalent of the options shown above, using the ``opts`` method that will be described shortly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict_spec = {'Curve': {'style':dict(color='blue')}, \n",
    "             'Curve.Sinusoid': {'style':dict(color='red')}, \n",
    "             'Curve.Sinusoid.Squared ': {'style':dict(color='green'),\n",
    "                                         'plot':dict(interpolation='steps-mid')}}\n",
    "\n",
    "dcurve = hv.Curve((xs, xs/3))\n",
    "dgroup_curve1 = hv.Curve((xs, np.sin(xs)), group='Sinusoid')\n",
    "dgroup_curve2 = hv.Curve((xs, np.sin(xs+np.pi/4)), group='Sinusoid')\n",
    "dlabel_curve = hv.Curve((xs, np.sin(xs)**2), group='Sinusoid', label='Squared')\n",
    "dlayout = dcurve * dgroup_curve1 * dgroup_curve2 * dlabel_curve\n",
    "dlayout.opts(dict_spec)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Although it is as simple as possible, this format is tedious and verbose to use: HoloViews allows you to specify *all* your options separate from your elements in one specifiation which means there is a minimum possible complexity. For this reason, the most commonly used format is the succinct string format describe below, which is parsed into the dictionary format behind the scenes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Simplified format"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The dictionary format above can be quite cumbersome to work with, therefore HoloViews provides a simpler ``.options`` method, which automatically distinguishes between ``plot``, ``style`` and ``norm`` options. We can take advantage of this to easily apply a mixture of options:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.Curve((xs, np.sin(xs))).options(width=500, color='red')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In a simple case like above where we are setting options that apply to the ``Curve`` element directly on a ``Curve`` we do not need to qualify further. However, when we are a composite object like an ``Overlay`` or ``Layout``, we have to be explicit about the object we are customizing, again using the ``type[[.group].label]`` specification."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve = hv.Curve((xs, np.sin(xs+np.pi/4)))\n",
    "stepped_curve = hv.Curve((xs, np.sin(xs)**2), group='Stepped')\n",
    "area = hv.Area((xs, np.sin(xs)**2))\n",
    "negative_area = hv.Area((xs, -(np.sin(xs)**2)), group='Negative')\n",
    "\n",
    "options = {'Curve': dict(width=500, color='red'),\n",
    "           'Curve.Stepped': dict(color='green', interpolation='steps-mid'),\n",
    "           'Area.Negative': dict(color='red')}\n",
    "\n",
    "(curve * stepped_curve + area * negative_area).options(options)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Yaml equivalent\n",
    "\n",
    "HoloViews defines its own string options specification format described in the next section. Although HoloViews does not support yaml directly, it is instructive to see how the dictionary format is expressed in [yaml](https://en.wikipedia.org/wiki/YAML):\n",
    "\n",
    "```\n",
    "Curve:\n",
    "    style: {color: 'blue'}\n",
    "Curve.Sinusoid:\n",
    "    style: {color: 'red'}\n",
    "Curve.Sinusoid.Squared:\n",
    "    style: {color: 'green'}\n",
    "    plot:  {interpolation: 'steps-mid'}\n",
    "```\n",
    "\n",
    "The reason HoloViews does not encourage the use of yaml is that yaml to literals and cannot express richer objects that can be used in options such as ``Cycle`` or ``Palette``."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### String format"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have seen the string format in this notebook as passed to the ``%%opts`` cell magic. Here is the definition of this format which is just a succinct way of specifing the dictionary format:\n",
    "\n",
    "```\n",
    "[[path] [normalization] [plotting options] [style options]]+\n",
    "\n",
    "path:             A dotted type.group.label specification\n",
    "                  (e.g. Image.Grayscale.Photo)\n",
    "\n",
    "normalization:    List of normalization options delimited by braces.\n",
    "                  One of | -axiswise | -framewise | +axiswise | +framewise |\n",
    "                  E.g. { +axiswise +framewise }\n",
    "\n",
    "plotting options: List of plotting option keywords delimited by\n",
    "                  square brackets. E.g. [show_title=False]\n",
    "\n",
    "style options:    List of style option keywords delimited by\n",
    "                  parentheses. E.g. (lw=10 marker='+')\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Applying options\n",
    "\n",
    "Here are the different ways of applying the options specifications and the accepted format:\n",
    "\n",
    "*  *The ``%%opts`` cell magic*: IPython specific syntax applies to displayed object *[string format]*\n",
    "* *The ``%opts`` line magic*: IPython specific syntax applied globally *[string format]*\n",
    "* *The ``.opts`` method*: Pure python method of HoloViews objects *[string format, dictionary format]*.\n",
    "* *The ``hv.opts`` utility*: Pure python equivalent to ``%opts`` and ``%%opts`` *[string format, dictionary format]*\n",
    "\n",
    "\n",
    "In the notebook environment, the recommended approach is to use ``%opts`` for global settings at the top of the notebook, then ``%%opts`` to customize the output of specific cells and finally the ``.opts`` method as necessary. The ``hv.opts`` utility is mostly intended for use in Python scripts.\n",
    "\n",
    "#### ``%%opts``\n",
    "\n",
    "As shown in the examples above, customizes a particular HoloViws output displayed in a code cell. This application is not global and persists for that object which means settings stay in place if the object is re-displayed. Only accepts the string specification format. All cell magics need to appear *above* any code in the cells they are used in and cannot be used if there is no code in the cell. Only accepts the string specification syntax.\n",
    "\n",
    "#### ``%opts``\n",
    "\n",
    "The ``%opts`` line magic is used to set global settings at the level of the notebook they are used in. If there are options you want to use throughout a notebook, they should be specified with ``%opts`` at the top of the notebook. Line magics can appear anywhere at the start of a line, whether in isolation or anywhere inside a cell containing code. Only accepts the string specification syntax.\n",
    "\n",
    "#### ``.opts`` method\n",
    "\n",
    "Used to specify options to a specific holoviews object. Useful to parameterize options programatically that can be hard to specify otherwise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.Overlay([hv.Curve((xs, np.sin(xs)**(i+1))).opts(style=dict(color=color, alpha=(i+1)/3.0)) \n",
    "            for (i, color) in enumerate(['red','green','blue'])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``.options`` method\n",
    "\n",
    "Similar to the ``.opts`` method but allows the use of the simplified, flattened specification of keywords:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.Overlay([hv.Curve((xs, np.sin(xs)**(i+1))).options(color=color, alpha=(i+1)/3.0)\n",
    "            for (i, color) in enumerate(['red','green','blue'])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "Note that you can split the dictionary format into groups using ``style``, ``plot`` and ``norm`` keywords."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "#### ``hv.opts`` utility\n",
    "\n",
    "The ``hv.opts`` utility is for use in Python scripts. It can accept an object as an argment in which case it behaves like the ``%%opts`` cell magic or if it isn't supplied an object, it behaves like the ``%opts`` line magic. Accepts either the string or dict specification format."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Customizing output\n",
    "\n",
    "In addition to style and plot options, you may want to specify the active plotting extension, the output format, the output size and other similar options. Here is how you can do this:\n",
    "\n",
    "\n",
    "*  *The ``%%output`` cell magic*: IPython specific syntax applies to displayed object *[string format]*\n",
    "* *The ``%output`` line magic*: IPython specific syntax applied globally *[string format]*\n",
    "* *The ``hv.output`` utility*: Pure python equivalent to ``%output`` and ``%%output`` *[string format, dictionary format]*.\n",
    "\n",
    "### Selecting a plotting extension\n",
    "\n",
    "At the start of this user guide, we used:\n",
    "\n",
    "```\n",
    "hv.extension('bokeh', 'matplotlib')\n",
    "```\n",
    "\n",
    "This *enabled* both the bokeh and matplotlib plotting extensions but *activated* the bokeh extension as it was listed first. We can switch to matplotlib for a single cell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output backend='matplotlib'\n",
    "hv.Curve((xs, np.cos(xs)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because we used the cell magic, matplotlib was only used for that cell and bokeh is still active. We can now change the size of the ``label_curve`` we customized in bokeh earlier (with the ``%%opts`` cell magic) using the ``output`` cell magic and save it to an HTML file in disk by supplying a filename:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output size=80 filename='small_curve'\n",
    "label_curve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ``%output``\n",
    "\n",
    "The ``%output`` line magic acts exactly like the ``%%output`` cell magic except it acts globally. For instance, you can use this at the top of a notebook to make all visualizations bigger or smaller.\n",
    "\n",
    "### ``hv.output`` utility\n",
    "\n",
    "The ``hv.output`` utility is the pure Python version of the magics which can accept keyword dictionaries as well as the string format used above. If supplied with an object, acts like the cell magic but this will have no effect unless a filename is supplied to save output to disk (see below). If not object is supplied, acts like the ``%output`` line magic.\n",
    "\n",
    "\n",
    "### Other output options\n",
    "\n",
    "Here are some of the other options supported by the output utilities and magics:\n",
    "\n",
    "* ``size``: Output size as a percentage of the original size.\n",
    "* ``filename``: Cell magic or ``hv.output`` utility only. Save output to given filename.\n",
    "* ``fig``: Output format for single frames. Options are backend dependent.\n",
    "* ``holomap``: Display format for ``HoloMap``, can be 'widgets', 'scrubber' (DVD-style controls), or (animated) 'gif'\n",
    "* ``dpi``: The output resolution (where applicable)\n",
    "\n",
    "Using the output and options system together, you should be able to customize your plots to your liking in a succinct manner.\n"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
