HoloViews is designed to be both highly customizable, allowing you to control how your visualizations appear, but also to enforce a strong separation between your data (with any semantically associated metadata, like type, dimension names, and description) and all options related purely to visualization. This separation allows HoloViews objects to be generated easily by external programs, without giving them a dependency on any plotting or windowing libraries. It also helps make it completely clear which parts of your code deal with the actual data, and which are just about displaying it nicely, which becomes very important for complex visualizations that become more complicated than your data itself.

To achieve this separation, HoloViews stores visualization options independently from your data, and applies the options only when rendering the data to a file on disk, a GUI window, or an IPython notebook cell.

This tutorial gives an overview of the different types of options available, how to find out more about them, and how to set them in both regular Python and using the IPython magic interface that is shown elsewhere in the tutorials.

Example objects

First, we'll create some HoloViews data objects ready to visualize:


In [ ]:
import numpy as np
import holoviews as hv
hv.notebook_extension()

x,y = np.mgrid[-50:51, -50:51] * 0.1
image = hv.Image(np.sin(x**2+y**2), group="Function", label="Sine") 

coords = [(0.1*i, np.sin(0.1*i)) for i in range(100)]
curve = hv.Curve(coords)

curves = {phase: hv.Curve([(0.1*i, np.sin(phase+0.1*i)) for i in range(100)])
          for phase in [0, np.pi/2, np.pi, np.pi*3/2]}

waves = hv.HoloMap(curves)

layout = image + curve

Rendering and saving objects from Python

To illustrate how to do plotting independently of IPython, we'll generate and save a plot directly to disk. First, let's create a renderer object that will render our files to SVG (for static figures) or GIF (for animations):


In [ ]:
renderer = hv.Store.renderers['matplotlib'].instance(fig='svg', holomap='gif')

We could instead have used the default Store.renderer, but that would have been PNG format. Using this renderer, we can save any HoloViews object as SVG or GIF:


In [ ]:
renderer.save(layout, 'example_I')

That's it! The renderer builds the figure in matplotlib, renders it to SVG, and saves that to "example_I.svg" on disk. Everything up to this point would have worked the same in IPython or in regular Python, even with no display available. But since we're in IPython Notebook at the moment, we can check whether the exporting worked, by loading the file back into the notebook:


In [ ]:
from IPython.display import SVG
SVG(filename='example_I.svg')

You can use this workflow for generating HoloViews visualizations directly from Python, perhaps as a part of a set of scripts that you run automatically, e.g. to put your results up on a web server as soon as data is generated. But so far, this plot just uses all the default options, with no customization. How can we change how the plot will appear when we render it?

HoloViews visualization options

HoloViews provides three categories of visualization options that can be set by the user. In this section we will first describe the different kinds of options, then later sections show you how to list the supported options of each type for a given HoloViews object or class, and how to change them in Python or IPython.

style options:

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 ).

For whichever backend has been selected, HoloViews can tell you which options are supported, but you will need to see the plotting library's own documentation (e.g. matplotlib, bokeh) for the details of their use.

HoloViews has been designed to be easily extensible to additional backends in the future, such as Plotly, Cairo, VTK, or D3.js, and if one of those backends were selected then the supported style options would differ.

plot options:

Each of the various HoloViews plotting classes declares various Parameters 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. 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.

norm options:

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.

There are currently only two norm options supported, axiswise and framewise, but they can be applied to any of the various object types in HoloViews to specify a huge range of different normalization options.

For a given category or group of HoloViews objects, if axiswise is True, normalization will be computed independently for all items in that category that have their own axes, such as different Image plots or Curve plots. If axiswise is False, all such objects are normalized together.

For a given category or group of HoloViews objects, if framewise is True, normalization of any HoloMap objects included is done independently per frame rendered -- each frame will appear as it would if it were extracted from the HoloMap and plotted separately. If framewise is False (the default), all frames in a given HoloMap are normalized together, so that you can see strength differences over the course of the animation.

As described below, these options can be controlled precisely and in any combination to make sure that HoloViews displays the data of most interest, ignoring irrelevant differences and highlighting important ones.

Finding out which options are available for an object

For the norm options, no further online documentation is provided, because all of the various visualization classes support only the two options described above. But there are a variety of ways to get the list of supported style options and detailed documentation for the plot options for a given component.

First, for any Python class or object in HoloViews, you can use holoviews.help(object-or-class, visualization=False) to find out about its parameters. For instance, these parameters are available for our Image object, shown with their current value (or default value, for a class), data type, whether it can be changed by the user (if it is constant, read-only, etc.), and bounds if any:


In [ ]:
hv.help(image, visualization=False)

This information can be useful, but we have explicitly suppressed information regarding the visualization parameters -- these all report metadata about your data, not about anything to do with plotting directly. That's because the normal HoloViews components have nothing to do with plotting; they are just simple containers for your data and a small amount of metadata.

Instead, the plotting implementation and its associated parameters are kept in completely separate Python classes and objects. To find out about visualizing a HoloViews component like an Image, you can simply use the help command holoviews.help(object-or-class) that looks up the code that plots that particular type of component, and then reports the style and plot options available for it.

For our image example, holoviews.help first finds that image is of type Image, then looks in its database to find that Image visualization is handled by the RasterPlot class (which users otherwise rarely need to access directly). holoviews.help then shows information about what objects are available to customize (either the object itself, or the items inside a container), followed by a brief list of style options supported by a RasterPlot, and a list of plot options (which are all the parameters of a RasterPlot). As this list of plot options is very long by default, here is an example that uses the pattern argument to limit the results to the options referencing the string 'bounds':


In [ ]:
hv.help(image, pattern='bounds')

The pattern option is particularly useful in conjunction with recursive=True which helps when searching for information across the different levels of a composite object. Note that the pattern argument supports Python's regular expression syntax and may also be used together with the visualization=False option.

Supported style options

As you can see, HoloViews lists the currently allowed style options, but provides no further documentation because these settings are implemented by matplotlib and described at the matplotlib site. Note that matplotlib actually accepts a huge range of additional options, but they are not listed as being allowed because those options are not normally meaningful for this plot type. But if you know of a specific matplotlib option not on the list and really want to use it, you can add it manually to the list of supported options using Store.add_style_opts(holoviews-component-class, ['matplotlib-option ...']). For instance, if you want to use the filternorm parameter with this image object, you would run Store.add_style_opts(Image, ['filternorm']). This will add the new option to the corresponding plotting class RasterPlot, ready for use just like any other style option:


In [ ]:
hv.Store.add_style_opts(hv.Image, ['filternorm'])
# To check that it worked:
RasterPlot = renderer.plotting_class(hv.Image)
print(RasterPlot.style_opts)

Changing plot options at the class level

Any parameter in HoloViews can be set on an object or on the class of the object, so any of the above plot options can be set like:


In [ ]:
RasterPlot.colorbar=True
RasterPlot.set_param(show_title=False,show_frame=True)

Here .set_param() allows you to set multiple parameters conveniently, but it works the same as the single-parameter .colorbar example above it. Setting these values at the class level affects all previously created and to-be-created plotting objects of this type, unless specifically overridden via Store as described below.

Note that if you look at the source code for a particular plotting class, you will only see some of the parameters it supports. The rest, such as show_frame above, are defined in a superclass of the given object. The Reference Manual shows the complete list of parameters available for any given class (those labeled param in the manual), but it can be an overwhelming list since it includes all superclasses, all the metadata about each parameter, etc. The holoviews.help command with visualization=True not only provides a much more concise listing, it can will also provide style options not available in the Reference Manual, by using the database to determine which plotting class is associated with this object.

Because setting these parameters at the class level does not provide much control over individual plots, HoloViews provides a much more flexible system using the OptionTree mechanisms described below, which can override these class defaults according to the more specific HoloViews object type, group, and label attributes.

The rest of the sections show how to change any of the above options, once you have found the right one using the suitable call to holoviews.help.

Controlling options from Python

Once you know the name of the option you want to change, and the value you want to change it to, there are a number of ways to customize your plot.

For the Python output to SVG example above, you can specify the options for a given type using keywords supplying a dictionary for any of the above option categories. You can see that the colormap changes when we supply that style option and render a new SVG:


In [ ]:
renderer.save(layout, 'example_II', style=dict(Image={'cmap':'Blues'}),
                                    plot= dict(Image={'yaxis':None}))
SVG(filename='example_II.svg')

As before, the SVG call is simply to display it here in the notebook; the actual image is saved on disk and then loaded back in here for display.

You can see that the image now has a colorbar, because we set colorbar=True on the RasterPlot class, that it has become blue, because we set the matplotlib cmap style option in the renderer.save call, and that the y axis has been disabled, because we set the plot option yaxis to None (which is normally 'left' by default, as you can see in the default value for RasterPlot's parameter yaxis above). Hopefully you can see that once you know the option value you want to use, it can be provided easily.

You can also create a whole set of options separately, perhaps holding a large collection of preferred values, and apply it whenever you wish to save:


In [ ]:
options={'Image.Function.Sine': {'plot':dict(fig_size=50), 'style':dict(cmap='jet')}}
renderer.save(layout, 'example_III',options=options)
SVG(filename='example_III.svg')

Here you can see that the y axis has returned, because our previous setting to turn it off was just for the call to renderer.save. But we still have a colorbar, because that parameter was set at the class level, for all future plots of this type. Note that this form of option setting, while more verbose, accepts the full {type}[.{group}[.{label}]] syntax, like 'Image.Function.Sine' or 'Image.Function', while the shorter keyword approach above only supports the class, like 'Image'.

Note that for the options dictionary, the option nesting is inverted compared to the keyword approach: the outermost dictionary is by key (Image, or Image.Function.Sines), with the option categories underneath. You can see that with this mechanism, we can specify the options even for subobjects of a container, as long as we can specify them with an appropriate key.

There's also another way to customize options in Python that lets you build up customizations incrementally. To do this, you can associate a particular set of options persistently with a particular HoloViews object, even if that object is later combined with other objects into a container. Here a new copy of the object is created, with the given set of options (using either the keyword or options= format above) bound to it:


In [ ]:
green_sine = image(style={'cmap':'Greens'})

Here we could save the object to SVG just as before, but in this case we can skip a step and simply view it directly in the notebook:


In [ ]:
green_sine

To customize options of individual components in composite objects like Overlays or Layouts you can either specify the options on each individual component or specify which object to customize using the {type}[.{group}[.{label}]] syntax.


In [ ]:
(image + curve)(style={'Image.Function.Sine': dict(cmap='Reds'), 'Curve': dict(color='indianred')})

Both IPython notebook and renderer.save() use the same mechanisms for keeping track of the options, so they will give the same results. Specifically, what happens when you "bind" a set of options to an object is that there is an integer ID stored in the object (green_sine in this case), and a corresponding entry with that ID is stored in a database of options called an OptionTree (kept in holoviews.core.options.Store). The object itself is otherwise unchanged, but then if that object is later used in another container, etc. it will retain its ID and therefore its customization. Any customization stored in an OptionTree will override any class attribute defaults set like RasterGridPlot.border=5 above. This approach lets HoloViews keep track of any customizations you want to make, without ever affecting your actual data objects.

If the same object is later customized again to create a new customized object, the old customizations will be copied, and then the new customizations applied. The new customizations will thus override the old, while retaining any previous customizations not specified in the new step.

In this way, it is possible to build complex objects with arbitrary customization, step by step. As mentioned above, it is also possible to customize objects already combined into a complex container, just by specifying an option for a suitable key (e.g. 'Image.Function.Sine' above). This flexible system should allow for any level of customization that is needed.

Finally, there is one more way to apply options that is a mix of the above approaches -- temporarily assign a new ID to the object and apply a set of customizations during a specific portion of the code. To illustrate this, we'll create a new Image object called 'Cosine':


In [ ]:
cosine = hv.Image(np.cos(x**2+y**2), group="Function", label="Cosine")

In [ ]:
with hv.StoreOptions.options(cosine, options={'Image':{'style':{'cmap':'Reds'}}}):
    data, info = renderer(cosine)
print(info)
SVG(data)

Here the result is in red as it was generated in the context of a 'Reds' colormap but if we display cosine again outside the scope of the with statement, it retains the default settings:


In [ ]:
cosine

Note that if we want to use this context manager to set new options on the existing green_sine object, you must specify that the options apply to a specific Image by stating the applicable group and label:


In [ ]:
with hv.StoreOptions.options(green_sine, options={'Image.Function.Sine':{'style':{'cmap':'Purples'}}}):
    data, info = renderer(green_sine)
print(info)
SVG(data)

Now the result inside the context is purple but elswhere green_sine remains green. If the group and label had not been specified above, the specific customization applied earlier (setting the green colormap) would take precedence over the general settings of Image. For this reason, it is important to know the appropriate precedence of new customizations, or else you can just always specify the object group and label to make sure the new settings override the old ones.

Controlling options in IPython using %%opts and %opts

The above sections describe how to set all of the options using regular Python. Similar functionality is provided in IPython, but with a more convenient syntax based on an IPython magic command:


In [ ]:
%%opts Curve style(linewidth=8) Image style(interpolation='bilinear') plot[yaxis=None] norm{+framewise}
layout

The %%opts magic works like the pure-Python option for associating options with an object, except that it works on the item in the IPython cell, and it affects the item directly rather than making a copy or applying only in scope. Specifically, it assigns a new ID number to the object returned from this cell, and makes a new OptionTree containing the options for that ID number.

If the same layout object is used later in the notebook, even within a complicated container object, it will retain the options set on it.

The options accepted are just the same as for the Python version, but specified more succinctly:

%%opts target-specification style(styleoption=val ...) plot[plotoption=val ...] norm{+normoption -normoption...}

Here key lets you specify the object type (e.g. Image), and optionally its group (e.g. Image.Function) or even both group and label (e.g. Image.Function.Sine), if you want to control options very precisely. There is also an even further abbreviated syntax, because the special bracket types alone are enough to indicate which category of option is specified:

%%opts target-specification (styleoption=val ...) [plotoption=val ...] {+normoption -normoption ...}

Here parentheses indicate style options, square brackets indicate plot options, and curly brackets indicate norm options (with +axiswise and +framewise indicating True for those values, and -axiswise and -framewise indicating False). Additional target-specifications and associated options of each type for that target-specification can be supplied at the end of this line. This ultra-concise syntax is used throughout the other tutorials, because it helps minimize the code needed to specify the plotting options, and helps make it very clear that these options are handled separately from the actual data.

Here we demonstrate the concise syntax by customizing the style and plot options of the Curve in the layout:


In [ ]:
%%opts Curve (color='r') [fontsize={'xlabel':15, 'ticks':8}] 
layout

The color of the curve has been changed to red and the fontsizes of the x-axis label and all the tick labels have been modified. The fontsize is an important plot option, and you can find more information about the available options in the fontsize documentation above.

The %%opts magic is designed to allow incremental customization, which explains why the curve in the cell above has retained the increased thickness specified earlier. To reset all the customizations that have been applied to an object, you can create a fresh, uncustomized copy as follows:


In [ ]:
layout()

The %opts "line" magic (with one %) works just the same as the %%opts "cell" magic, but it changes the global default options for all future cells, allowing you to choose a new default colormap, line width, etc.

Apart from its brevity, a big benefit of using the IPython magic syntax %%opts or %opts is that it is fully tab-completable. Each of the options that is currently available will be listed if you press <TAB> when you are ready to write it, which makes it much easier to find the right parameter. Of course, you will still need to consult the full holoviews.help documentation (described above) to see the type, allowable values, and documentation for each option, but the tab completion should at least get you started and is great for helping you remember the list of options and see which options are available.

You can even use the succinct IPython-style specification directly in your Python code if you wish, but it requires the external pyparsing library (which is already available if you are using matplotlib):


In [ ]:
from holoviews.ipython.parser import OptsSpec
renderer.save(image + waves, 'example_V', 
              options=OptsSpec.parse("Image (cmap='gray')"))

There is also a special IPython syntax for listing the visualization options for a plotting object in a pop-up window that is equivalent to calling holoviews.help(object):


In [ ]:
%%output info=True
curve

The line-magic version of this syntax %output info=True is particularly useful for learning about components using the notebook, because it will keep a window open with the available options for each object updated as you do <shift-Enter> in each cell. E.g. you can go through each of the components in the Elements or Containers tutorials in this way, to see what options are offered by each without having to type anything for each one.

Separating data and visualization options

Hopefully you see from this tutorial how HoloViews enforces a strict separation between your data, stored in HoloViews Element and container objects, and your plotting options, stored in dictionaries or OptionTrees. Finding the right options is easiest in IPython, because of <TAB> completion, but the same options are available in pure Python as well, with or without a display, allowing you to automate any part of the process of visualization and analysis.