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

Title: Segments Element#

Dependencies Bokeh

Backends Bokeh, Matplotlib

import numpy as np
import holoviews as hv
from holoviews import dim

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:

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:

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

Display and style the Element:

seg.opts(color='k', line_width=10)

A fractal tree#

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:

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:

s.opts(xaxis=None, yaxis=None, height=400, width=400,toolbar='above',
       color=np.log10(1+dim('c')), cmap='Greens', line_width=15)

Cantor set#

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