{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import holoviews as hv\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HoloViews ordinarily hides the plotting machinery from the user. This allows for very quick iteration over different visualizations to explore a dataset, however it is often important to customize the precise details of a plot. HoloViews makes it very easy to customize existing plots, or even create completely novel plots. This manual will provide a general overview of the plotting system.\n", "\n", "The separation of the data from the precise details of the visualization is one of the core principles of the HoloViews. [``Elements``](http://holoviews.org/reference/index.html#elements) provide thin wrappers around chunks of actual data, while [containers](http://holoviews.org/reference/index.html#containers) allow composing these Elements into overlays, layouts, grids and animations/widgets. Each Element or container type has a corresponding plotting class, which renders a visual representation of the data for a particular backend. While the precise details of the implementation differ between backends to accommodate the vastly different APIs plotting backends provide, many of the high-level details are shared across backends.\n", "\n", "# The Store object\n", "\n", "The association between an Element or container and the backend specific plotting class is held on the global ``Store`` object. The ``Store`` object holds a ``registry`` of plot objects for each backend. We can view the registry for each backend by accessing ``Store.registry`` directly:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import holoviews.plotting.mpl\n", "list(hv.Store.registry['matplotlib'].items())[0:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Store object provides a global registry not only for the plots themselves but also creates an entry in the OptionsTree for that particular backend. This allows options for that backend to be validated and enables setting plot, style and normalization options via the [Options system](03-Applying_Customizations.ipynb) system. We can view the ``OptionsTree`` object by requesting it from the store. We'll make a copy with just the first few entries so we can view the structure of the tree:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "opts = hv.Store.options(backend='matplotlib')\n", "hv.core.options.OptionTree(opts.items()[0:10], groups=['plot', 'style', 'norm'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Saving and rendering\n", "\n", "The easiest entry points to rendering a HoloViews object either to the backend specific representation (e.g. a matplotlib figure) or directly to file are the ``hv.render`` and ``hv.save`` functions. Both are shortcuts for using an actual ``Renderer`` object, which will be introduced in the next section. To start with we will create a simple object, a ``Scatter`` element:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "curve = hv.Curve(range(10))\n", "curve" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This abstract and declarative representation of a ``Curve`` can be turned into an actual plot object, defaulting to the currently selected (or default) backend:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = hv.render(curve)\n", "print(type(fig))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By providing an explicit ``backend`` keyword the plot can be rendered using a different backend." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = hv.render(curve, backend='bokeh')\n", "print(type(p))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This can often be useful to customize a plot in more detail by tweaking styling in ways that are not directly supported by HoloViews. An alternative to ``hv.render`` in case you do not want to fully switch to the underlying plotting API are ``hooks``, which are a plot option on all elements. These allow defining hooks which modify a plot after it has been rendered in the backend but before it is displayed.\n", "\n", "A ``hook`` is given the HoloViews plot instance and the currently rendered element and thereby provides access to the rendered plot object to apply any customizations and changes which are not exposed by HoloViews:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def hook(plot, element):\n", " # Allows accessing the backends figure object\n", " plot.state\n", " \n", " # The handles contain common plot objects\n", " plot.handles\n", "\n", "curve = curve.opts(hooks=[hook])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hooks allow for extensive tweaking of objects before they are finished rendering, without having to entirely abandon HoloViews' API and render the backend's plot object manually.\n", "\n", "In much the same way the ``hv.save`` function allows exporting plots straight to a file, by default inferring the format from the file extension:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.save(curve, 'curve.png')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like ``hv.render`` the ``hv.save`` function also allows specifying an explicit backend." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.save(curve, 'curve.html', backend='bokeh')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Additionally for ambiguous file extensions such as HTML it may be necessary to specify an explicit fmt to override the default, e.g. in the case of 'html' output the widgets will default to ``fmt='widgets'``, which may be changed to scrubber widgets using ``fmt='scrubber'``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Renderers\n", "\n", "HoloViews provides a general ``Renderer`` baseclass, which defines a general interface to render the output from different backends to a number of standard output formats such as ``png``, ``html`` or ``svg``. The ``__call__`` method on the Renderer automatically looks up and instantiates the registered plotting classes for an object it is passed and then returns the output in the requested format. To make this a bit clearer we'll break this down step by step. First we'll get a handle on the ``MPLRenderer`` and create an object to render.\n", "\n", "Renderers aren't registered with the Store until the corresponding backends have been imported. Loading the notebook extension with ``hv.notebook_extension('matplotlib')`` is one way of loading a backend and registering a renderer. Another is to simply import the corresponding plotting module as we did above." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.Store.renderers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is one way to access a Renderer, another is to instantiate a Renderer instance directly, allowing you to override some of the default plot options." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "renderer = hv.plotting.mpl.MPLRenderer.instance(dpi=120)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The recommended way to get a handle on a renderer is to use the ``hv.renderer`` function which will also handle imports for you:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.renderer('matplotlib')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with a Renderer\n", "\n", "A ``Renderer`` in HoloViews is responsible for instantiating a HoloViews plotting class. It does this by looking up the plotting class in the ``Store.registry``:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.Store.registry['matplotlib'][hv.Curve]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we create a ``Curve`` we can instantiate a plotting class from it using the ``Renderer.get_plot`` method:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "curve = hv.Curve(range(10))\n", "curve_plot = renderer.get_plot(curve)\n", "curve_plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will revisit how to work with ``Plot`` instances later. For now all we need to know is that they are responsible for translating the HoloViews object (like the ``Curve``) into a backend specific plotting object, accessible on ``Plot.state``:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(type(curve_plot.state), curve_plot.state)\n", "curve_plot.state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In case of the matplotlib backend this is a ``Figure`` object. However the ``Renderer`` ignores the specific representation of the plot, instead providing a unified interface to translating it into a representation that can displayed, i.e. either an image format or an HTML representation.\n", "\n", "In this way we can convert the curve directly to its ``png`` representation by calling the ``Renderer`` with the object and the format:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import display_png\n", "png, info = renderer(curve, fmt='png')\n", "print(info)\n", "display_png(png, raw=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
hv.help
to print a detailed docstring.\n",
"