Building Composite Objects#

The reference gallery shows examples of each of the container types in HoloViews. As you work through this guide, it will be useful to look at the description of each type there.

This guide shows you how to combine the various container types, in order to build data structures that can contain all of the data that you want to visualize or analyze, in an extremely flexible way. For instance, you may have a large set of measurements of different types of data (numerical, image, textual notations, etc.) from different experiments done on different days, with various different parameter values associated with each one. HoloViews can store all of this data together, which will allow you to select just the right bit of data “on the fly” for any particular analysis or visualization, by indexing, slicing, selecting, and sampling in this data structure.

Nesting hierarchy #

To illustrate the full functionality provided, we will create an example of the maximally nested object structure currently possible with HoloViews:

import numpy as np
import holoviews as hv
hv.extension('bokeh')
np.random.seed(10)

def sine_curve(phase, freq, amp, power, samples=102):
    xvals = [0.1* i for i in range(samples)]
    return [(x, amp*np.sin(phase+freq*x)**power) for x in xvals]

phases =      [0, np.pi/2, np.pi, 3*np.pi/2]
powers =      [1,2,3]
amplitudes =  [0.5,0.75, 1.0]
frequencies = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75]


gridspace = hv.GridSpace(kdims=['Amplitude', 'Power'], group='Parameters', label='Sines')

for power in powers:
    for amplitude in amplitudes:
        holomap = hv.HoloMap(kdims='Frequency')
        for frequency in frequencies:
            sines = {phase : hv.Curve(sine_curve(phase, frequency, amplitude, power))
                     for phase in phases}
            ndoverlay = hv.NdOverlay(sines, kdims='Phase').relabel(group='Phases',
                                                                   label='Sines', depth=1)
            overlay = ndoverlay * hv.Points([(i,0) for i in range(0,10)], group='Markers', label='Dots')
            holomap[frequency] = overlay
        gridspace[amplitude, power] = holomap

penguins = hv.RGB.load_image('../reference/elements/assets/penguins.png').relabel(group="Family", label="Penguin")

layout = gridspace + penguins.opts(axiswise=True)

This code produces what looks like a relatively simple animation of two side-by-side figures, but is actually a deeply nested data structure:

layout

To help us understand this structure, here is a schematic for us to refer to as we unpack this object, level by level:

Everything that is displayable in HoloViews has this same basic hierarchical structure, although any of the levels can be omitted in simpler cases, and many different Element types (not containers) can be substituted for any other.

Since HoloViews 1.3.0, you are allowed to build data-structures that violate this hierarchy (e.g., you can put Layout objects into HoloMaps) but the resulting object cannot be displayed. Instead, you will be prompted with a message to call the collate method. Using the collate method will allow you to generate the appropriate object that correctly obeys the hierarchy shown above, so that it can be displayed.

As shown in the diagram, there are three different types of container involved:

  • Basic Element: elementary HoloViews object containing raw data in an external format like Numpy or pandas.

  • Homogeneous container (UniformNdMapping): collections of Elements or other HoloViews components that are all the same type. These are indexed using array-style key access with values sorted along some dimension(s), e.g. [0.50] or ["a",7.6].

  • Heterogeneous container (AttrTree): collections of data of different types, e.g. different types of Element. These are accessed by categories using attributes, e.g. .Parameters.Sines, which does not assume any ordering of a dimension.

We will now go through each of the containers of these different types, at each level.

Layout Level#

Above, we have already viewed the highest level of our data structure as a Layout. Here is the representation of the entire Layout object, which reflects all the levels shown in the diagram:

print(layout)
:Layout
   .Parameters.Sines :GridSpace   [Amplitude,Power]
      :HoloMap   [Frequency]
         :Overlay
            .Phases.Sines :NdOverlay   [Phase]
               :Curve   [x]   (y)
            .Markers.Dots :Points   [x,y]
   .Family.Penguin   :RGB   [x,y]   (R,G,B,A)

In the examples below, we will unpack this data structure using attribute access (explained in the Annotating Data user guide) as well as indexing and slicing (explained in the Indexing and Selecting Data user guide).

GridSpace Level#

Elements within a Layout, such as the GridSpace in this example, are reached via attribute access:

layout.Parameters.Sines