Segments

Download this notebook from GitHub (right-click to download).


Title: Segments Element

Dependencies Bokeh

Backends Bokeh, Matplotlib

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

Segments visualizes a collection of line segments, each starting at a position (x0, y0) and ending at a position (x1, y1). To specify it, we hence need four key dimensions, listed in the order (x0, y0, x1, y1), and an arbitrary number of value dimensions as attributes to each line segment.

Basic usage

Declare mock data:

In [2]:
event = ['A', 'B']
data = dict(
    start=[np.datetime64('1999'), np.datetime64('2001')],
    end=[np.datetime64('2010'), np.datetime64('2020')],
    start_event = event,
    end_event = event
)

Define the Segments:

In [3]:
seg = hv.Segments(data, [hv.Dimension('start', label='Year'), 
                         hv.Dimension('start_event', label='Event'), 'end', 'end_event'])

Display and style the Element:

In [4]:
seg.opts(color='k', line_width=10)
Out[4]:
A fractal tree
In [5]:
from functools import reduce
def tree(N):
    """
    Generates fractal tree up to branch N.
    """
    # x0, y0, x1, y1, level
    branches = [(0, 0, 0, 1)]
    theta = np.pi/5 # branching angle
    r = 0.5 # length ratio between successive branches
    
    # Define function to grow successive branches given previous branch and branching angle
    angle = lambda b: np.arctan2(b[3]-b[1], b[2]-b[0])
    length = lambda b: np.sqrt((b[3]-b[1])**2 + (b[2]-b[0])**2)
    grow = lambda b, ang: (b[2], b[3], 
                           b[2] + r*length(b)*np.cos(angle(b)+ang), 
                           b[3] + r*length(b)*np.sin(angle(b)+ang))
    ctr = 1
    while ctr<=N:
        yield branches
        ctr += 1
        branches = [[grow(b, theta), grow(b, -theta)] for b in branches]
        branches = reduce(lambda i, j: i+j, branches)
    
t = reduce(lambda i, j: i+j, tree(14))
data = np.array(t[1:])

Declare a Segments Element and add an additional value dimension c that we can use for styling:

In [6]:
s = hv.Segments(np.c_[data, np.arange(len(data))], ['x', 'y', 'x1', 'y1'], 'c')

Now, let's style the Element into a digital broccoli painting:

In [7]:
s.opts(xaxis=None, yaxis=None, height=400, width=400,toolbar='above',
       color=np.log10(1+dim('c')), cmap='Greens', line_width=15)
Out[7]:
Cantor set
In [8]:
def cantor(N):
    """
    Generates a Cantor set up to iteration N, cutting out the middle 9th of each interval
    at each step.
    """
    y = 0
    intervals = [(0, 1, y)]
    while y<=N:
        yield intervals
        dx = (intervals[0][1]-intervals[0][0])/9*4
        y += 1
        intervals = [[(i[0], i[0]+dx, y), (i[1]-dx, i[1], y)] for i in intervals]
        intervals = reduce(lambda i, j: i+j, intervals)

cl = reduce(lambda i, j: i+j, cantor(12))

x0, x1, y = zip(*cl)
data = np.array(cl)
In [9]:
s = hv.Segments((x0, y, x1, y, y), vdims=['c'])
s.opts(xaxis=None, yaxis=None, height=160, width=500, 
       toolbar='above', line_width=8, color=dim('c'), cmap='fire_r')
Out[9]:

Download this notebook from GitHub (right-click to download).