Composing Elements

In [1]:
import numpy as np
import holoviews as hv
hv.extension('bokeh')

Instantly viewable HoloViews objects include elements (discussed already) and containers (collections of elements or other containers). Here we'll introduce two types of containers for collecting viewable objects, each typically created from the existing objects using a convenient operator syntax:

  1. Layout (+): A collection of any HoloViews objects to be displayed side by side.
  2. Overlay (*): A collection of HoloViews objects to be displayed overlaid on one another with the same axes.

The Layout and Overlay containers allow you to mix types in any combination, and have an ordering but no numerical or categorical key dimension with which to index the objects. In contrast, the Dimensioned containers discussed later, such as HoloMap , GridSpace, NdOverlay, and NdLayout, do not allow mixed types, and each item has an associated numerical or categorical index (key).

Because you can compose a mix of any HoloViews elements into layouts and overlays, these types of container are very common, which is why they have dedicated composition operators. This user guide describes how you can build and organize your data using these two types of composition.

To show how layouts and overlays work with heterogeneous types, we will use these two elements throughout this notebook:

In [2]:
xs = [0.1* i for i in range(100)]
curve =  hv.Curve((xs, [np.sin(x) for x in xs]))
scatter =  hv.Scatter((xs[::5], np.linspace(0,1,20)))

1. Layout

A Layout can contain any HoloViews object except an NdLayout. (See Building Composite Objects for the full details about the ways containers can be composed.)

You can build a Layout from two or more HoloViews objects of any type by using the + operator:

In [3]:
curve + scatter
Out[3]:

In this example, we have a Layout composed of a Curve element and a Scatter element, and they happen to share the same x and y dimensions.

Building a Layout from a list

If the + syntax is not convenient, you can also build a Layout using its constructor directly, which is useful if you want to create a Layout of an arbitrary length:

In [4]:
curve_list   = [hv.Curve((xs, [np.sin(f*x) for x in xs])) for f in [0.5, 0.75]]
scatter_list = [hv.Scatter((xs[::5], f*np.linspace(0,1,20))) for f in [-0.5, 0.5]]

layout = hv.Layout(curve_list + scatter_list).cols(2)
layout
Out[4]:

Note the use of the .cols method to specify the number of columns, wrapping to the next row in scanline order (left to right, then top to bottom).

A Layout has two-level attribute access

Layout and Overlay are tree-based data structures that can hold arbitrarily heterogenous collections of HoloViews objects, and are quite different from the dictionary-like dimensioned containers (which will be described in later guides).

As mentioned previously in Annotating Data, HoloViews objects have string group and label parameters, which can be used to select objects in the Layout using two-level attribute access. First let us see how to index the above example, where the group and label parameters were left unspecified on creation:

In [5]:
print(layout)
:Layout
   .Curve.I    :Curve   [x]   (y)
   .Curve.II   :Curve   [x]   (y)
   .Scatter.I  :Scatter   [x]   (y)
   .Scatter.II :Scatter   [x]   (y)

As you can see, the layout object consists of four different elements, each mapping from x to y. You can use the "dot" syntax shown in the repr to select individual elements from the layout:

In [6]:
layout2 = layout.Curve.I + layout.Scatter.II
layout2
Out[6]:

Here we create a second layout by indexing two elements from our earlier layout object and using + between them. We see that the first level of indexing is the group string (which defaults to the element class name) followed by the label, which wasn't set and is therefore mapped to an automatically generated Roman numeral (I,II,III,IV, etc.).

As group and label were again not specified, our new Layout will also use Curve.I for the curve, but as there is only one scatter element, it will have Scatter.I to index the scatter:

In [7]:
layout2.Scatter.I
Out[7]:

Using group and label with Layout

Now let's return to the first simple layout example, this time setting a group and label as introduced in the Annotating Data guide:

In [8]:
xs = [0.1* i for i in range(100)]

low_freq  = hv.Curve((xs, [np.sin(x) for x in xs]),    group='Sinusoid',      label='Low Frequency')
linpoints = hv.Scatter((xs[::5], np.linspace(0,1,20)), group='Linear Points', label='Demo')

labelled = low_freq + linpoints
labelled
Out[8]:

As you can see, the group and label are used for titling the plots. They also determine how the objects are accessed:

In [9]:
labelled.Linear_Points.Demo + labelled.Sinusoid.Low_Frequency
Out[9]:

You are encouraged to use the group and label names as appropriate for organizing your own data. They should let you easily refer to groups of data that are meaningful to your domain, e.g. for Applying Customizations.

2. Overlay

An Overlay can contain any HoloViews elements, but the only container type it can contain is NdOverlay. Building Composite Objects provides the full details on how containers can be composed.

Other than being composed with * and displaying elements together in the same space, Overlay shares many of the same concepts as layout. The rest of this section will show the overlay equivalents of the manipulations shown above for layout.

First, composition with * instead of + results in a single overlaid plot, rather than side-by-side plots:

In [10]:
curve * scatter
Out[10]:

Building Overlay from a list

An Overlay can be built explicitly from a list, just like a Layout:

In [11]:
curve_list   = [hv.Curve((xs, [np.sin(f*x) for x in xs])) for f in [0.5, 0.75]]
scatter_list = [hv.Scatter((xs[::5], f*np.linspace(0,1,20))) for f in [-0.5, 0.5]]
overlay = hv.Overlay(curve_list + scatter_list)
overlay
Out[11]:

As you can see, a special feature of Overlay compared to Layout is that overlays use color cycles to help keep the overlaid plots distinguishable, which you can learn about in Applying Customization.

Overlay also has two-level attribute access

Like Layout, Overlay is fundamentally a tree structure holding arbitrarily heterogenous HoloViews objects, unlike the dimensioned containers. Overlay objects also make use of the group and label parameters, introduced in Annotating Data, for two-level attribute access.

Once again, let us see how to index the above example where the group and label parameters were left unspecified:

In [12]:
print(overlay)
:Overlay
   .Curve.I    :Curve   [x]   (y)
   .Curve.II   :Curve   [x]   (y)
   .Scatter.I  :Scatter   [x]   (y)
   .Scatter.II :Scatter   [x]   (y)
In [13]:
overlay.Curve.I * overlay.Scatter.II
Out[13]:

Here we create a second overlay by indexing two elements from our earlier overlay object and using * between them. We see that the first level is the group string (which defaults to the element class name) followed by the label, which wasn't set and is therefore mapped to a Roman numeral.

Using group and label with Overlay

Now let's return to the first simple overlay example, this time setting group and label as introduced in the Annotating Data guide:

In [14]:
high_freq =  hv.Curve((xs, [np.sin(2*x) for x in xs]), group='Sinusoid', label='High Frequency')
labelled = low_freq * high_freq * linpoints
labelled
Out[14]:

Once again, this example follows the corresponding Layout example, although this time we added a high-frequency curve to demonstrate how group and label are now used to generate the legend (as opposed to the title, as it was for Layout).

The following example shows how group and label affect access:

In [15]:
labelled.Linear_Points.Demo * labelled.Sinusoid.High_Frequency * labelled.Sinusoid.Low_Frequency
Out[15]:

This new re-ordered Overlay switches the z-ordering as well as the legend color of the two sinusoidal curves. The colors and other plot options can be set for specific groups and labels as described in Applying Customizations.

Layouts of overlays

Of course, layouts work with both elements and overlays:

In [16]:
overlay + labelled + labelled.Sinusoid.Low_Frequency
Out[16]:

Tab completion

Both Layout and Overlay are designed to be easy to explore and inspect with tab completion. Try running:

overlay.[tab]

or

layout.[tab]

In a code cell and you should see the first levels of indexing (Curve and Scatter) conveniently listed at the top. If this is not the case, you may need to enable improved tab-completion as described in Configuring HoloViews.

Having seen how to compose viewable objects, the next section shows how to apply customizations.