Composing Elements¶
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:
-
Layout
(+
): A collection of any HoloViews objects to be displayed side by side. 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:
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:
curve + scatter
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:
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
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:
print(layout)
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:
layout2 = layout.Curve.I + layout.Scatter.II
layout2
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:
layout2.Scatter.I
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:
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
As you can see, the group and label are used for titling the plots. They also determine how the objects are accessed:
labelled.Linear_Points.Demo + labelled.Sinusoid.Low_Frequency
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:
curve * scatter
Building Overlay
from a list¶
An Overlay
can be built explicitly from a list, just like a Layout
:
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
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:
print(overlay)
overlay.Curve.I * overlay.Scatter.II
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:
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
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:
labelled.Linear_Points.Demo * labelled.Sinusoid.High_Frequency * labelled.Sinusoid.Low_Frequency
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:
overlay + labelled + labelled.Sinusoid.Low_Frequency
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.